@jaak.ai/stamps 2.2.0-dev.11 → 2.2.0-dev.13

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.
@@ -377,17 +377,30 @@ export class CameraService {
377
377
  }
378
378
  const constraints = { ...videoConstraints };
379
379
  if (capabilities.width && capabilities.height) {
380
- const maxWidth = Math.min(capabilities.width.max, 1920);
381
- const maxHeight = Math.min(capabilities.height.max, 1080);
382
380
  const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
383
- if (isTablet) {
384
- constraints.width = { ideal: Math.min(maxWidth, 1280) };
385
- constraints.height = { ideal: Math.min(maxHeight, 720) };
381
+ const isMobileDevice = this.deviceType === 'mobile' ||
382
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
383
+ // Optimize resolution based on device type to prevent "slow motion" effect
384
+ // on devices with high-resolution cameras
385
+ let targetWidth;
386
+ let targetHeight;
387
+ if (isMobileDevice && !isTablet) {
388
+ // Mobile phones: Use 720p max to ensure smooth detection performance
389
+ targetWidth = Math.min(capabilities.width.max, 1280);
390
+ targetHeight = Math.min(capabilities.height.max, 720);
391
+ }
392
+ else if (isTablet) {
393
+ // Tablets: Can handle slightly higher resolution
394
+ targetWidth = Math.min(capabilities.width.max, 1280);
395
+ targetHeight = Math.min(capabilities.height.max, 720);
386
396
  }
387
397
  else {
388
- constraints.width = { ideal: maxWidth };
389
- constraints.height = { ideal: maxHeight };
398
+ // Desktop: Can handle full HD
399
+ targetWidth = Math.min(capabilities.width.max, 1920);
400
+ targetHeight = Math.min(capabilities.height.max, 1080);
390
401
  }
402
+ constraints.width = { ideal: targetWidth };
403
+ constraints.height = { ideal: targetHeight };
391
404
  }
392
405
  // Configure autofocus if camera has the capability
393
406
  const capabilitiesAny = capabilities;
@@ -407,9 +420,28 @@ export class CameraService {
407
420
  await this.detectDeviceType();
408
421
  }
409
422
  const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
423
+ const isMobileDevice = this.deviceType === 'mobile' ||
424
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
425
+ // Optimized fallback constraints for device type
426
+ let fallbackWidth;
427
+ let fallbackHeight;
428
+ if (isMobileDevice && !isTablet) {
429
+ // Mobile phones: Use 720p for smooth performance
430
+ fallbackWidth = 1280;
431
+ fallbackHeight = 720;
432
+ }
433
+ else if (isTablet) {
434
+ fallbackWidth = 1280;
435
+ fallbackHeight = 720;
436
+ }
437
+ else {
438
+ // Desktop
439
+ fallbackWidth = 1920;
440
+ fallbackHeight = 1080;
441
+ }
410
442
  const fallbackConstraints = {
411
- width: { ideal: isTablet ? 1280 : 1920 },
412
- height: { ideal: isTablet ? 720 : 1080 }
443
+ width: { ideal: fallbackWidth },
444
+ height: { ideal: fallbackHeight }
413
445
  };
414
446
  if (this.selectedCameraId) {
415
447
  fallbackConstraints.deviceId = { exact: this.selectedCameraId };
@@ -1 +1 @@
1
- {"version":3,"file":"CameraService.js","sourceRoot":"","sources":["../../src/services/CameraService.ts"],"names":[],"mappings":"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","sourcesContent":["import { ICameraService, CameraInfo } from './interfaces/ICameraService';\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 // Optimization: Static caches for performance\n private static cameraCapabilitiesCache = new Map<string, boolean | string>();\n private static deviceEnumerationCache: {\n devices: MediaDeviceInfo[];\n timestamp: number;\n } | null = null;\n private static readonly CACHE_DURATION = 30000; // 30 seconds\n \n // Stream management for optimization\n private currentStreamPromise?: Promise<MediaStream>;\n private lastStreamConstraints?: MediaTrackConstraints;\n\n constructor(\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 \n return this.deviceType;\n }\n\n async enumerateDevices(): Promise<MediaDeviceInfo[]> {\n try {\n // Check cache first for optimization\n const now = Date.now();\n if (CameraService.deviceEnumerationCache &&\n (now - CameraService.deviceEnumerationCache.timestamp) < CameraService.CACHE_DURATION) {\n this.availableCameras = CameraService.deviceEnumerationCache.devices;\n await this.setInitialCameraPreference();\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n return this.availableCameras;\n }\n\n const permissionStatus = await this.checkCameraPermission();\n\n if (permissionStatus === 'denied') {\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 const allCameras = devices.filter(device => device.kind === 'videoinput');\n\n // Filter out virtual cameras\n this.availableCameras = allCameras.filter(camera => {\n const isVirtual = this.isVirtualCamera(camera);\n if (isVirtual) {\n console.log(`Virtual camera detected and filtered: ${camera.label}`);\n }\n return !isVirtual;\n });\n\n // Check if there are physical cameras available\n if (this.availableCameras.length === 0) {\n console.log('No physical cameras available');\n this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));\n return [];\n }\n\n // Cache the results for optimization\n CameraService.deviceEnumerationCache = {\n devices: this.availableCameras,\n timestamp: now\n };\n\n await this.setInitialCameraPreference();\n\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n\n return this.availableCameras;\n } catch (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.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 try {\n const finalConstraints = constraints || await this.getMaxResolution();\n\n // Optimization: Check if we can reuse existing stream with same constraints\n if (this.currentStreamPromise && this.constraintsEqual(finalConstraints, this.lastStreamConstraints)) {\n return await this.currentStreamPromise;\n }\n\n // Create new stream promise for lazy loading\n this.currentStreamPromise = navigator.mediaDevices.getUserMedia({\n video: finalConstraints,\n audio: false\n });\n\n this.lastStreamConstraints = finalConstraints;\n\n const stream = await this.currentStreamPromise;\n\n // Validate that active stream is not from a virtual camera\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n const settings = videoTrack.getSettings();\n const deviceId = settings.deviceId;\n if (deviceId) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const currentDevice = devices.find(device => device.deviceId === deviceId);\n if (currentDevice && this.isVirtualCamera(currentDevice)) {\n console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);\n stream.getTracks().forEach(track => track.stop());\n throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');\n }\n }\n }\n\n return stream;\n } catch (error) {\n\n // Fallback to basic constraints if getMaxResolution fails\n const basicConstraints: MediaTrackConstraints = {\n width: { ideal: 1280 },\n height: { ideal: 720 }\n };\n\n // Only add facingMode if not on desktop to avoid OverconstrainedError\n if (this.deviceType !== 'desktop' && this.preferredCameraFacing) {\n basicConstraints.facingMode = this.preferredCameraFacing;\n }\n\n this.currentStreamPromise = navigator.mediaDevices.getUserMedia({\n video: basicConstraints,\n audio: false\n });\n\n this.lastStreamConstraints = basicConstraints;\n\n const stream = await this.currentStreamPromise;\n\n // Validate fallback stream as well\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n const settings = videoTrack.getSettings();\n const deviceId = settings.deviceId;\n if (deviceId) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const currentDevice = devices.find(device => device.deviceId === deviceId);\n if (currentDevice && this.isVirtualCamera(currentDevice)) {\n console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);\n stream.getTracks().forEach(track => track.stop());\n throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');\n }\n }\n }\n\n return stream;\n }\n }\n\n async switchCamera(cameraId: string): Promise<void> {\n const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!selectedCamera) {\n await this.enumerateDevices();\n return;\n }\n\n // Validate that the target camera is not virtual\n if (this.isVirtualCamera(selectedCamera)) {\n console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);\n this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));\n return;\n }\n\n await this.setSelectedCamera(cameraId);\n }\n\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 async getCameraInfo(): Promise<{\n availableCameras: CameraInfo[];\n selectedCameraId: string | null;\n deviceType: string;\n isMultipleCamerasAvailable: boolean;\n preferredFacing: 'environment' | 'user' | null;\n }> {\n const availableCamerasWithAutofocus = await Promise.all(\n this.availableCameras.map(async (camera) => {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n return {\n id: camera.deviceId,\n label: camera.label || 'Unknown Camera',\n selected: camera.deviceId === this.selectedCameraId,\n hasAutofocus\n };\n })\n );\n\n return {\n availableCameras: availableCamerasWithAutofocus,\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 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 async setInitialCameraPreference(): Promise<void> {\n if (this.availableCameras.length === 0) return;\n \n \n if (this.preferredCamera === 'front') {\n await this.selectFrontCamera();\n } else if (this.preferredCamera === 'back') {\n await this.selectBackCamera();\n } else {\n await this.selectAutoCamera();\n }\n \n }\n\n private async checkCameraAutofocus(deviceId: string): Promise<boolean> {\n try {\n // Check cache first for optimization\n if (CameraService.cameraCapabilitiesCache.has(deviceId)) {\n const cached = CameraService.cameraCapabilitiesCache.get(deviceId)!;\n return typeof cached === 'boolean' ? cached : false;\n }\n\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { deviceId: { exact: deviceId } }\n });\n \n const videoTrack = stream.getVideoTracks()[0];\n const capabilities = videoTrack.getCapabilities() as any;\n \n stream.getTracks().forEach(track => track.stop());\n \n const hasFocusMode = capabilities.focusMode && Array.isArray(capabilities.focusMode) && capabilities.focusMode.length > 0;\n const hasAutofocus = hasFocusMode && (\n capabilities.focusMode.includes('continuous') || \n capabilities.focusMode.includes('auto')\n );\n \n // Cache the result for future use\n CameraService.cameraCapabilitiesCache.set(deviceId, hasAutofocus);\n \n return hasAutofocus;\n } catch (error) {\n // Cache negative result to avoid repeated failures\n CameraService.cameraCapabilitiesCache.set(deviceId, false);\n return false;\n }\n }\n\n private async selectFrontCamera(): Promise<void> {\n this.preferredCameraFacing = 'user';\n \n const frontCameras = this.availableCameras.filter(camera => {\n const label = camera.label.toLowerCase();\n return label.includes('front') || \n label.includes('user') ||\n label.includes('selfie') ||\n label.includes('frontal') ||\n (!label.includes('back') && !label.includes('rear') && !label.includes('environment'));\n });\n \n let selectedCamera = null;\n \n if (frontCameras.length > 0) {\n for (const camera of frontCameras) {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n if (hasAutofocus) {\n selectedCamera = camera;\n break;\n }\n }\n \n if (!selectedCamera) {\n selectedCamera = frontCameras[0];\n }\n }\n \n this.selectedCameraId = selectedCamera ? selectedCamera.deviceId : this.availableCameras[0].deviceId;\n }\n\n private async selectBackCamera(): Promise<void> {\n this.preferredCameraFacing = 'environment';\n \n const backCameras = this.availableCameras.filter(camera => {\n const label = camera.label.toLowerCase();\n return label.includes('back') || \n label.includes('rear') ||\n label.includes('environment') ||\n label.includes('trasera') ||\n label.includes('posterior');\n });\n \n let selectedCamera = null;\n \n if (backCameras.length > 0) {\n for (const camera of backCameras) {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n if (hasAutofocus) {\n selectedCamera = camera;\n break;\n }\n }\n \n if (!selectedCamera) {\n selectedCamera = backCameras[0];\n }\n } else if (this.availableCameras.length > 1) {\n const lastCamera = this.availableCameras[this.availableCameras.length - 1];\n const hasAutofocus = await this.checkCameraAutofocus(lastCamera.deviceId);\n selectedCamera = hasAutofocus ? lastCamera : this.availableCameras[this.availableCameras.length - 1];\n }\n \n this.selectedCameraId = selectedCamera ? selectedCamera.deviceId : this.availableCameras[0].deviceId;\n }\n\n private async selectAutoCamera(): Promise<void> {\n if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {\n await this.selectBackCamera();\n } else {\n // For desktop, prefer front camera (usually built-in webcam)\n await this.selectFrontCamera();\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 // Ensure device type is detected before determining constraints\n if (!this.deviceType || this.deviceType === 'desktop') {\n await this.detectDeviceType();\n }\n \n const videoConstraints: MediaTrackConstraints = {};\n \n // Priority 1: Always use deviceId if available (ensures specific camera with autofocus)\n if (this.selectedCameraId) {\n videoConstraints.deviceId = { exact: this.selectedCameraId };\n } \n // Priority 2: Use facingMode as fallback when no specific camera is selected\n else if (this.preferredCameraFacing) {\n const facingConstraint = this.deviceType === 'desktop' \n ? { ideal: this.preferredCameraFacing }\n : { exact: this.preferredCameraFacing };\n videoConstraints.facingMode = facingConstraint;\n } \n // Priority 3: Fallback to preferredCamera setting\n else {\n if (this.preferredCamera === 'front') {\n videoConstraints.facingMode = \"user\";\n } else if (this.preferredCamera === 'back') {\n videoConstraints.facingMode = \"environment\"; \n } else {\n videoConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? \"environment\" : \"user\";\n }\n }\n \n // Optimization: Use cached capabilities if available\n let capabilities: MediaTrackCapabilities;\n \n if (this.selectedCameraId && CameraService.cameraCapabilitiesCache.has(this.selectedCameraId + '_caps')) {\n capabilities = JSON.parse(CameraService.cameraCapabilitiesCache.get(this.selectedCameraId + '_caps') as string);\n } else {\n const tempStream = await navigator.mediaDevices.getUserMedia({ video: videoConstraints });\n const videoTrack = tempStream.getVideoTracks()[0];\n capabilities = videoTrack.getCapabilities();\n tempStream.getTracks().forEach(track => track.stop());\n \n // Cache capabilities for future use\n if (this.selectedCameraId) {\n CameraService.cameraCapabilitiesCache.set(this.selectedCameraId + '_caps', JSON.stringify(capabilities));\n }\n }\n \n const constraints: MediaTrackConstraints = { ...videoConstraints };\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 // Configure autofocus if camera has the capability\n const capabilitiesAny = capabilities as any;\n if (capabilitiesAny.focusMode && Array.isArray(capabilitiesAny.focusMode)) {\n if (capabilitiesAny.focusMode.includes('continuous')) {\n (constraints as any).focusMode = 'continuous';\n } else if (capabilitiesAny.focusMode.includes('auto')) {\n (constraints as any).focusMode = 'auto';\n }\n }\n \n return constraints;\n } catch (err) {\n // Fallback logic with minimal temporary streams\n if (!this.deviceType || this.deviceType === 'desktop') {\n await this.detectDeviceType();\n }\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 if (this.selectedCameraId) {\n fallbackConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n const facingConstraint = this.deviceType === 'desktop' \n ? { ideal: this.preferredCameraFacing }\n : { exact: this.preferredCameraFacing };\n fallbackConstraints.facingMode = facingConstraint;\n } else {\n if (this.preferredCamera === 'front') {\n fallbackConstraints.facingMode = \"user\";\n } else if (this.preferredCamera === 'back') {\n fallbackConstraints.facingMode = \"environment\"; \n } else {\n fallbackConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? \"environment\" : \"user\";\n }\n }\n \n return fallbackConstraints;\n }\n }\n\n\n // Optimization helper methods\n private constraintsEqual(a?: MediaTrackConstraints, b?: MediaTrackConstraints): boolean {\n if (!a || !b) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n // Cleanup method for optimization\n public static clearCaches(): void {\n CameraService.cameraCapabilitiesCache.clear();\n CameraService.deviceEnumerationCache = null;\n }\n\n // Method to invalidate device cache when needed\n public invalidateDeviceCache(): void {\n CameraService.deviceEnumerationCache = null;\n }\n\n private isVirtualCamera(device: MediaDeviceInfo): boolean {\n const label = device.label.toLowerCase();\n const virtualCameraIndicators = [\n 'obs',\n 'virtual',\n 'snap camera',\n 'manycam',\n 'xsplit',\n 'camtwist',\n 'ecamm',\n 'nvidia broadcast',\n 'droidcam',\n 'iriun',\n 'elgato',\n 'streamlabs',\n 'wirecast',\n 'zoom virtual',\n 'teams virtual',\n 'mmhmm',\n 'camo',\n 'reincubate camo',\n 'webcamoid',\n 'splitcam',\n 'cheese',\n 'guvcview',\n 'yawcam',\n 'webcam 7',\n 'altserver'\n ];\n\n return virtualCameraIndicators.some(indicator => label.includes(indicator));\n }\n\n}"]}
1
+ {"version":3,"file":"CameraService.js","sourceRoot":"","sources":["../../src/services/CameraService.ts"],"names":[],"mappings":"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","sourcesContent":["import { ICameraService, CameraInfo } from './interfaces/ICameraService';\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 // Optimization: Static caches for performance\n private static cameraCapabilitiesCache = new Map<string, boolean | string>();\n private static deviceEnumerationCache: {\n devices: MediaDeviceInfo[];\n timestamp: number;\n } | null = null;\n private static readonly CACHE_DURATION = 30000; // 30 seconds\n \n // Stream management for optimization\n private currentStreamPromise?: Promise<MediaStream>;\n private lastStreamConstraints?: MediaTrackConstraints;\n\n constructor(\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 \n return this.deviceType;\n }\n\n async enumerateDevices(): Promise<MediaDeviceInfo[]> {\n try {\n // Check cache first for optimization\n const now = Date.now();\n if (CameraService.deviceEnumerationCache &&\n (now - CameraService.deviceEnumerationCache.timestamp) < CameraService.CACHE_DURATION) {\n this.availableCameras = CameraService.deviceEnumerationCache.devices;\n await this.setInitialCameraPreference();\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n return this.availableCameras;\n }\n\n const permissionStatus = await this.checkCameraPermission();\n\n if (permissionStatus === 'denied') {\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 const allCameras = devices.filter(device => device.kind === 'videoinput');\n\n // Filter out virtual cameras\n this.availableCameras = allCameras.filter(camera => {\n const isVirtual = this.isVirtualCamera(camera);\n if (isVirtual) {\n console.log(`Virtual camera detected and filtered: ${camera.label}`);\n }\n return !isVirtual;\n });\n\n // Check if there are physical cameras available\n if (this.availableCameras.length === 0) {\n console.log('No physical cameras available');\n this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));\n return [];\n }\n\n // Cache the results for optimization\n CameraService.deviceEnumerationCache = {\n devices: this.availableCameras,\n timestamp: now\n };\n\n await this.setInitialCameraPreference();\n\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n\n return this.availableCameras;\n } catch (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.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 try {\n const finalConstraints = constraints || await this.getMaxResolution();\n\n // Optimization: Check if we can reuse existing stream with same constraints\n if (this.currentStreamPromise && this.constraintsEqual(finalConstraints, this.lastStreamConstraints)) {\n return await this.currentStreamPromise;\n }\n\n // Create new stream promise for lazy loading\n this.currentStreamPromise = navigator.mediaDevices.getUserMedia({\n video: finalConstraints,\n audio: false\n });\n\n this.lastStreamConstraints = finalConstraints;\n\n const stream = await this.currentStreamPromise;\n\n // Validate that active stream is not from a virtual camera\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n const settings = videoTrack.getSettings();\n const deviceId = settings.deviceId;\n if (deviceId) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const currentDevice = devices.find(device => device.deviceId === deviceId);\n if (currentDevice && this.isVirtualCamera(currentDevice)) {\n console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);\n stream.getTracks().forEach(track => track.stop());\n throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');\n }\n }\n }\n\n return stream;\n } catch (error) {\n\n // Fallback to basic constraints if getMaxResolution fails\n const basicConstraints: MediaTrackConstraints = {\n width: { ideal: 1280 },\n height: { ideal: 720 }\n };\n\n // Only add facingMode if not on desktop to avoid OverconstrainedError\n if (this.deviceType !== 'desktop' && this.preferredCameraFacing) {\n basicConstraints.facingMode = this.preferredCameraFacing;\n }\n\n this.currentStreamPromise = navigator.mediaDevices.getUserMedia({\n video: basicConstraints,\n audio: false\n });\n\n this.lastStreamConstraints = basicConstraints;\n\n const stream = await this.currentStreamPromise;\n\n // Validate fallback stream as well\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack) {\n const settings = videoTrack.getSettings();\n const deviceId = settings.deviceId;\n if (deviceId) {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const currentDevice = devices.find(device => device.deviceId === deviceId);\n if (currentDevice && this.isVirtualCamera(currentDevice)) {\n console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);\n stream.getTracks().forEach(track => track.stop());\n throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');\n }\n }\n }\n\n return stream;\n }\n }\n\n async switchCamera(cameraId: string): Promise<void> {\n const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!selectedCamera) {\n await this.enumerateDevices();\n return;\n }\n\n // Validate that the target camera is not virtual\n if (this.isVirtualCamera(selectedCamera)) {\n console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);\n this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));\n return;\n }\n\n await this.setSelectedCamera(cameraId);\n }\n\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 async getCameraInfo(): Promise<{\n availableCameras: CameraInfo[];\n selectedCameraId: string | null;\n deviceType: string;\n isMultipleCamerasAvailable: boolean;\n preferredFacing: 'environment' | 'user' | null;\n }> {\n const availableCamerasWithAutofocus = await Promise.all(\n this.availableCameras.map(async (camera) => {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n return {\n id: camera.deviceId,\n label: camera.label || 'Unknown Camera',\n selected: camera.deviceId === this.selectedCameraId,\n hasAutofocus\n };\n })\n );\n\n return {\n availableCameras: availableCamerasWithAutofocus,\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 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 async setInitialCameraPreference(): Promise<void> {\n if (this.availableCameras.length === 0) return;\n \n \n if (this.preferredCamera === 'front') {\n await this.selectFrontCamera();\n } else if (this.preferredCamera === 'back') {\n await this.selectBackCamera();\n } else {\n await this.selectAutoCamera();\n }\n \n }\n\n private async checkCameraAutofocus(deviceId: string): Promise<boolean> {\n try {\n // Check cache first for optimization\n if (CameraService.cameraCapabilitiesCache.has(deviceId)) {\n const cached = CameraService.cameraCapabilitiesCache.get(deviceId)!;\n return typeof cached === 'boolean' ? cached : false;\n }\n\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { deviceId: { exact: deviceId } }\n });\n \n const videoTrack = stream.getVideoTracks()[0];\n const capabilities = videoTrack.getCapabilities() as any;\n \n stream.getTracks().forEach(track => track.stop());\n \n const hasFocusMode = capabilities.focusMode && Array.isArray(capabilities.focusMode) && capabilities.focusMode.length > 0;\n const hasAutofocus = hasFocusMode && (\n capabilities.focusMode.includes('continuous') || \n capabilities.focusMode.includes('auto')\n );\n \n // Cache the result for future use\n CameraService.cameraCapabilitiesCache.set(deviceId, hasAutofocus);\n \n return hasAutofocus;\n } catch (error) {\n // Cache negative result to avoid repeated failures\n CameraService.cameraCapabilitiesCache.set(deviceId, false);\n return false;\n }\n }\n\n private async selectFrontCamera(): Promise<void> {\n this.preferredCameraFacing = 'user';\n \n const frontCameras = this.availableCameras.filter(camera => {\n const label = camera.label.toLowerCase();\n return label.includes('front') || \n label.includes('user') ||\n label.includes('selfie') ||\n label.includes('frontal') ||\n (!label.includes('back') && !label.includes('rear') && !label.includes('environment'));\n });\n \n let selectedCamera = null;\n \n if (frontCameras.length > 0) {\n for (const camera of frontCameras) {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n if (hasAutofocus) {\n selectedCamera = camera;\n break;\n }\n }\n \n if (!selectedCamera) {\n selectedCamera = frontCameras[0];\n }\n }\n \n this.selectedCameraId = selectedCamera ? selectedCamera.deviceId : this.availableCameras[0].deviceId;\n }\n\n private async selectBackCamera(): Promise<void> {\n this.preferredCameraFacing = 'environment';\n \n const backCameras = this.availableCameras.filter(camera => {\n const label = camera.label.toLowerCase();\n return label.includes('back') || \n label.includes('rear') ||\n label.includes('environment') ||\n label.includes('trasera') ||\n label.includes('posterior');\n });\n \n let selectedCamera = null;\n \n if (backCameras.length > 0) {\n for (const camera of backCameras) {\n const hasAutofocus = await this.checkCameraAutofocus(camera.deviceId);\n if (hasAutofocus) {\n selectedCamera = camera;\n break;\n }\n }\n \n if (!selectedCamera) {\n selectedCamera = backCameras[0];\n }\n } else if (this.availableCameras.length > 1) {\n const lastCamera = this.availableCameras[this.availableCameras.length - 1];\n const hasAutofocus = await this.checkCameraAutofocus(lastCamera.deviceId);\n selectedCamera = hasAutofocus ? lastCamera : this.availableCameras[this.availableCameras.length - 1];\n }\n \n this.selectedCameraId = selectedCamera ? selectedCamera.deviceId : this.availableCameras[0].deviceId;\n }\n\n private async selectAutoCamera(): Promise<void> {\n if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {\n await this.selectBackCamera();\n } else {\n // For desktop, prefer front camera (usually built-in webcam)\n await this.selectFrontCamera();\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 // Ensure device type is detected before determining constraints\n if (!this.deviceType || this.deviceType === 'desktop') {\n await this.detectDeviceType();\n }\n \n const videoConstraints: MediaTrackConstraints = {};\n \n // Priority 1: Always use deviceId if available (ensures specific camera with autofocus)\n if (this.selectedCameraId) {\n videoConstraints.deviceId = { exact: this.selectedCameraId };\n } \n // Priority 2: Use facingMode as fallback when no specific camera is selected\n else if (this.preferredCameraFacing) {\n const facingConstraint = this.deviceType === 'desktop' \n ? { ideal: this.preferredCameraFacing }\n : { exact: this.preferredCameraFacing };\n videoConstraints.facingMode = facingConstraint;\n } \n // Priority 3: Fallback to preferredCamera setting\n else {\n if (this.preferredCamera === 'front') {\n videoConstraints.facingMode = \"user\";\n } else if (this.preferredCamera === 'back') {\n videoConstraints.facingMode = \"environment\"; \n } else {\n videoConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? \"environment\" : \"user\";\n }\n }\n \n // Optimization: Use cached capabilities if available\n let capabilities: MediaTrackCapabilities;\n \n if (this.selectedCameraId && CameraService.cameraCapabilitiesCache.has(this.selectedCameraId + '_caps')) {\n capabilities = JSON.parse(CameraService.cameraCapabilitiesCache.get(this.selectedCameraId + '_caps') as string);\n } else {\n const tempStream = await navigator.mediaDevices.getUserMedia({ video: videoConstraints });\n const videoTrack = tempStream.getVideoTracks()[0];\n capabilities = videoTrack.getCapabilities();\n tempStream.getTracks().forEach(track => track.stop());\n \n // Cache capabilities for future use\n if (this.selectedCameraId) {\n CameraService.cameraCapabilitiesCache.set(this.selectedCameraId + '_caps', JSON.stringify(capabilities));\n }\n }\n \n const constraints: MediaTrackConstraints = { ...videoConstraints };\n \n if (capabilities.width && capabilities.height) {\n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n const isMobileDevice = this.deviceType === 'mobile' ||\n /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);\n\n // Optimize resolution based on device type to prevent \"slow motion\" effect\n // on devices with high-resolution cameras\n let targetWidth: number;\n let targetHeight: number;\n\n if (isMobileDevice && !isTablet) {\n // Mobile phones: Use 720p max to ensure smooth detection performance\n targetWidth = Math.min(capabilities.width.max, 1280);\n targetHeight = Math.min(capabilities.height.max, 720);\n } else if (isTablet) {\n // Tablets: Can handle slightly higher resolution\n targetWidth = Math.min(capabilities.width.max, 1280);\n targetHeight = Math.min(capabilities.height.max, 720);\n } else {\n // Desktop: Can handle full HD\n targetWidth = Math.min(capabilities.width.max, 1920);\n targetHeight = Math.min(capabilities.height.max, 1080);\n }\n\n constraints.width = { ideal: targetWidth };\n constraints.height = { ideal: targetHeight };\n }\n \n // Configure autofocus if camera has the capability\n const capabilitiesAny = capabilities as any;\n if (capabilitiesAny.focusMode && Array.isArray(capabilitiesAny.focusMode)) {\n if (capabilitiesAny.focusMode.includes('continuous')) {\n (constraints as any).focusMode = 'continuous';\n } else if (capabilitiesAny.focusMode.includes('auto')) {\n (constraints as any).focusMode = 'auto';\n }\n }\n \n return constraints;\n } catch (err) {\n // Fallback logic with minimal temporary streams\n if (!this.deviceType || this.deviceType === 'desktop') {\n await this.detectDeviceType();\n }\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n const isMobileDevice = this.deviceType === 'mobile' ||\n /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);\n\n // Optimized fallback constraints for device type\n let fallbackWidth: number;\n let fallbackHeight: number;\n\n if (isMobileDevice && !isTablet) {\n // Mobile phones: Use 720p for smooth performance\n fallbackWidth = 1280;\n fallbackHeight = 720;\n } else if (isTablet) {\n fallbackWidth = 1280;\n fallbackHeight = 720;\n } else {\n // Desktop\n fallbackWidth = 1920;\n fallbackHeight = 1080;\n }\n\n const fallbackConstraints: MediaTrackConstraints = {\n width: { ideal: fallbackWidth },\n height: { ideal: fallbackHeight }\n };\n \n if (this.selectedCameraId) {\n fallbackConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n const facingConstraint = this.deviceType === 'desktop' \n ? { ideal: this.preferredCameraFacing }\n : { exact: this.preferredCameraFacing };\n fallbackConstraints.facingMode = facingConstraint;\n } else {\n if (this.preferredCamera === 'front') {\n fallbackConstraints.facingMode = \"user\";\n } else if (this.preferredCamera === 'back') {\n fallbackConstraints.facingMode = \"environment\"; \n } else {\n fallbackConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? \"environment\" : \"user\";\n }\n }\n \n return fallbackConstraints;\n }\n }\n\n\n // Optimization helper methods\n private constraintsEqual(a?: MediaTrackConstraints, b?: MediaTrackConstraints): boolean {\n if (!a || !b) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n // Cleanup method for optimization\n public static clearCaches(): void {\n CameraService.cameraCapabilitiesCache.clear();\n CameraService.deviceEnumerationCache = null;\n }\n\n // Method to invalidate device cache when needed\n public invalidateDeviceCache(): void {\n CameraService.deviceEnumerationCache = null;\n }\n\n private isVirtualCamera(device: MediaDeviceInfo): boolean {\n const label = device.label.toLowerCase();\n const virtualCameraIndicators = [\n 'obs',\n 'virtual',\n 'snap camera',\n 'manycam',\n 'xsplit',\n 'camtwist',\n 'ecamm',\n 'nvidia broadcast',\n 'droidcam',\n 'iriun',\n 'elgato',\n 'streamlabs',\n 'wirecast',\n 'zoom virtual',\n 'teams virtual',\n 'mmhmm',\n 'camo',\n 'reincubate camo',\n 'webcamoid',\n 'splitcam',\n 'cheese',\n 'guvcview',\n 'yawcam',\n 'webcam 7',\n 'altserver'\n ];\n\n return virtualCameraIndicators.some(indicator => label.includes(indicator));\n }\n\n}"]}
@@ -530,17 +530,30 @@ class CameraService {
530
530
  }
531
531
  const constraints = { ...videoConstraints };
532
532
  if (capabilities.width && capabilities.height) {
533
- const maxWidth = Math.min(capabilities.width.max, 1920);
534
- const maxHeight = Math.min(capabilities.height.max, 1080);
535
533
  const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
536
- if (isTablet) {
537
- constraints.width = { ideal: Math.min(maxWidth, 1280) };
538
- constraints.height = { ideal: Math.min(maxHeight, 720) };
534
+ const isMobileDevice = this.deviceType === 'mobile' ||
535
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
536
+ // Optimize resolution based on device type to prevent "slow motion" effect
537
+ // on devices with high-resolution cameras
538
+ let targetWidth;
539
+ let targetHeight;
540
+ if (isMobileDevice && !isTablet) {
541
+ // Mobile phones: Use 720p max to ensure smooth detection performance
542
+ targetWidth = Math.min(capabilities.width.max, 1280);
543
+ targetHeight = Math.min(capabilities.height.max, 720);
544
+ }
545
+ else if (isTablet) {
546
+ // Tablets: Can handle slightly higher resolution
547
+ targetWidth = Math.min(capabilities.width.max, 1280);
548
+ targetHeight = Math.min(capabilities.height.max, 720);
539
549
  }
540
550
  else {
541
- constraints.width = { ideal: maxWidth };
542
- constraints.height = { ideal: maxHeight };
551
+ // Desktop: Can handle full HD
552
+ targetWidth = Math.min(capabilities.width.max, 1920);
553
+ targetHeight = Math.min(capabilities.height.max, 1080);
543
554
  }
555
+ constraints.width = { ideal: targetWidth };
556
+ constraints.height = { ideal: targetHeight };
544
557
  }
545
558
  // Configure autofocus if camera has the capability
546
559
  const capabilitiesAny = capabilities;
@@ -560,9 +573,28 @@ class CameraService {
560
573
  await this.detectDeviceType();
561
574
  }
562
575
  const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;
576
+ const isMobileDevice = this.deviceType === 'mobile' ||
577
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
578
+ // Optimized fallback constraints for device type
579
+ let fallbackWidth;
580
+ let fallbackHeight;
581
+ if (isMobileDevice && !isTablet) {
582
+ // Mobile phones: Use 720p for smooth performance
583
+ fallbackWidth = 1280;
584
+ fallbackHeight = 720;
585
+ }
586
+ else if (isTablet) {
587
+ fallbackWidth = 1280;
588
+ fallbackHeight = 720;
589
+ }
590
+ else {
591
+ // Desktop
592
+ fallbackWidth = 1920;
593
+ fallbackHeight = 1080;
594
+ }
563
595
  const fallbackConstraints = {
564
- width: { ideal: isTablet ? 1280 : 1920 },
565
- height: { ideal: isTablet ? 720 : 1080 }
596
+ width: { ideal: fallbackWidth },
597
+ height: { ideal: fallbackHeight }
566
598
  };
567
599
  if (this.selectedCameraId) {
568
600
  fallbackConstraints.deviceId = { exact: this.selectedCameraId };
@@ -18557,7 +18589,7 @@ class LicenseValidationService {
18557
18589
  }
18558
18590
  }
18559
18591
 
18560
- const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.license-error-container{display:flex;align-items:center;justify-content:center;padding:40px 20px;height:100%;width:100%;background:#f5f7fa;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif}.license-error-card{background:#ffffff;border-radius:8px;box-shadow:0 2px 12px rgba(0, 0, 0, 0.1);max-width:480px;width:100%;padding:48px 32px;text-align:center}.license-error-icon{color:#dc3545;margin-bottom:24px}.license-error-title{color:#2c3e50;font-size:24px;font-weight:600;margin:0 0 16px 0}.license-error-message{color:#495057;font-size:16px;line-height:1.6;margin:0 0 24px 0}.license-error-help{color:#6c757d;font-size:14px;margin:0 0 32px 0}.license-error-help a{color:#007bff;text-decoration:none;font-weight:500}.license-error-help a:hover{text-decoration:underline}.license-error-footer{color:#adb5bd;font-size:12px;font-weight:500;padding-top:24px;border-top:1px solid #e9ecef}@media (max-width: 640px){.license-error-card{padding:32px 24px}.license-error-title{font-size:20px}.license-error-message{font-size:14px}}";
18592
+ const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background-color:#000;border:none;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);color:#ffffff;font-size:12px;font-weight:500;text-align:center;white-space:normal;padding:13px;max-width:300px;z-index:20;height:fit-content;width:fit-content;animation:slideInFromTopCentered 0.3s ease-out}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.back-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:320px}.back-capture-buttons{display:flex;gap:12px;align-items:center;justify-content:center;flex-wrap:wrap}.capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.capture-button:hover{background:#f8f9fa}.capture-button:active{background:#e9ecef;transform:translateY(1px)}.capture-button:disabled,.capture-button.primary-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.back-capture-section{bottom:16px;max-width:300px}.back-capture-buttons{flex-direction:column;gap:8px;width:100%}.capture-button,.back-capture-buttons .skip-button{width:100%;max-width:200px;padding:10px 20px;font-size:13px}.capture-button:disabled,.capture-button.primary-button:disabled,.skip-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.guide-text.performance-warning-text{animation:gentlePulse 2s ease-in-out infinite;background:rgba(255, 107, 107, 0.3);border:1px solid rgba(255, 107, 107, 0.5)}@keyframes gentlePulse{0%,100%{transform:translate(-50%, -50%) scale(1);background:rgba(255, 107, 107, 0.3)}50%{transform:translate(-50%, -50%) scale(1.02);background:rgba(255, 107, 107, 0.4)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.skip-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.2);border-top:2px solid #333333;border-radius:50%;animation:spin 1s linear infinite;display:inline-block;margin-right:8px;vertical-align:middle}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes slideInFromTopCentered{0%{opacity:0;transform:translate(-50%, -50%) translateY(-8px) scale(0.95)}100%{opacity:1;transform:translate(-50%, -50%) translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}.guide-text{font-size:11px;padding:4px 8px;border-radius:8px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromTop 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}.manual-capture-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.manual-capture-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.manual-capture-button:hover{background:#f8f9fa}.manual-capture-button:active{background:#e9ecef;transform:translateY(1px)}.manual-capture-button:disabled{cursor:not-allowed !important;transform:none !important;opacity:0.7 !important;transition:none !important}@media (max-width: 480px){.manual-capture-section{bottom:16px;max-width:280px}.manual-capture-button{padding:10px 20px;font-size:13px}.manual-capture-button:disabled{opacity:0.7 !important;cursor:not-allowed !important}}.license-error-container{display:flex;align-items:center;justify-content:center;padding:40px 20px;height:100%;width:100%;background:#f5f7fa;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif}.license-error-card{background:#ffffff;border-radius:8px;box-shadow:0 2px 12px rgba(0, 0, 0, 0.1);max-width:480px;width:100%;padding:48px 32px;text-align:center}.license-error-icon{color:#dc3545;margin-bottom:24px}.license-error-title{color:#2c3e50;font-size:24px;font-weight:600;margin:0 0 16px 0}.license-error-message{color:#495057;font-size:16px;line-height:1.6;margin:0 0 24px 0}.license-error-help{color:#6c757d;font-size:14px;margin:0 0 32px 0}.license-error-help a{color:#007bff;text-decoration:none;font-weight:500}.license-error-help a:hover{text-decoration:underline}.license-error-footer{color:#adb5bd;font-size:12px;font-weight:500;padding-top:24px;border-top:1px solid #e9ecef}@media (max-width: 640px){.license-error-card{padding:32px 24px}.license-error-title{font-size:20px}.license-error-message{font-size:14px}}";
18561
18593
 
18562
18594
  const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H {
18563
18595
  constructor() {
@@ -18679,6 +18711,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
18679
18711
  MAX_FAILURES = 30;
18680
18712
  lastInferenceTime = 0;
18681
18713
  MIN_INFERENCE_INTERVAL = 50;
18714
+ MOBILE_MIN_INFERENCE_INTERVAL = 100; // Higher interval for mobile to prevent slow motion
18682
18715
  performanceHistory = [];
18683
18716
  PERFORMANCE_HISTORY_SIZE = 10;
18684
18717
  // Sistema simplificado de monitoreo de rendimiento
@@ -19543,9 +19576,14 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
19543
19576
  return;
19544
19577
  }
19545
19578
  this.frameSkipCounter = 0;
19546
- // Throttle inference
19579
+ // Throttle inference - use higher interval for mobile devices to prevent slow motion effect
19547
19580
  const currentTime = Date.now();
19548
- if (currentTime - this.lastInferenceTime < this.MIN_INFERENCE_INTERVAL) {
19581
+ const isMobileDevice = this.cameraInfoWithAutofocus.deviceType === 'mobile' ||
19582
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
19583
+ const isTablet = this.cameraInfoWithAutofocus.deviceType === 'tablet' ||
19584
+ (/iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768);
19585
+ const minInterval = (isMobileDevice && !isTablet) ? this.MOBILE_MIN_INFERENCE_INTERVAL : this.MIN_INFERENCE_INTERVAL;
19586
+ if (currentTime - this.lastInferenceTime < minInterval) {
19549
19587
  if (captureState.step !== 'completed') {
19550
19588
  this.animationId = requestAnimationFrame(() => this.detectFrame());
19551
19589
  }
@@ -19698,7 +19736,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
19698
19736
  isCapturing: false
19699
19737
  };
19700
19738
  const cameraInfo = this.cameraInfoWithAutofocus;
19701
- return (h("div", { key: 'a52b452a7aef6574a5e4b74a88c7bd3f9d45fd15', class: "detector-container" }, !this.licenseValid && this.licenseError && (h("div", { key: '18a99579e0b74ceb2cf1c4c3e21a864d2ec7d944', class: "license-error-container" }, h("div", { key: '3431722f4f47824c172a5b6f3a2ad9bd3bf377f2', class: "license-error-card" }, h("svg", { key: 'bf3854de3ba865a58c5c91cfd7a85d3d725be0df', 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: '209e3a2a22333ec90e102a7156eb644b529f47c7', 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: '7b0dc1cd29a9055e552885b23f4537c19932556d', d: "M12 8V12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round" }), h("circle", { key: '8ea686ebfb31a5744303f313fd104c03c02a5472', cx: "12", cy: "16", r: "0.5", fill: "currentColor", stroke: "currentColor", "stroke-width": "1" })), h("h2", { key: '7e0ec3ec23b80176cbec9c706999746d62486f3c', class: "license-error-title" }, "Licencia Requerida"), h("p", { key: '261a91e2c20feb09319dc4f319fb79611cc36a46', class: "license-error-message" }, this.licenseError), h("p", { key: '2e3cc8da0f4a8ebfd52db469b0f0900514d1d7f8', class: "license-error-help" }, "Contacte a soporte: ", h("a", { key: '283eea87e2f69dce1ea7376e3a63fed780633499', href: "mailto:support@jaak.ai" }, "support@jaak.ai")), h("div", { key: 'b4227ed25a4fb4d953c753671d27ac8c931a435f', class: "license-error-footer" }, "JAAK Stamps")))), this.licenseValid && (h("div", { key: 'cb33431cf6992f6e7466273406f729c42c1cbcdb', class: "video-container" }, h("video", { key: 'b44befd61ad616fd36fe1e98fd089a2443bc65b6', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '8cee95ddab5f771a87d43fa434a3ba0c241d2f3e', 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: {
19739
+ 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: {
19702
19740
  position: 'absolute',
19703
19741
  left: `${box.x}px`,
19704
19742
  top: `${box.y}px`,
@@ -19707,9 +19745,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
19707
19745
  border: '2px solid #32406C',
19708
19746
  pointerEvents: 'none',
19709
19747
  boxSizing: 'border-box'
19710
- } })))), this.isMaskReady && (h("div", { key: '00a749ad234294695d9a292911b41fdacafd219a', class: "overlay-mask" }, h("div", { key: 'e344df8a709fca5225985ccc9f8def75ddbd393c', class: "card-outline" }, h("div", { key: '2dcae9e414f2b7131397d49a3d5ecabfc7cd5eb8', class: "side side-top" }), h("div", { key: '48ac3f06fa4a2c83741df378719911f682811caa', class: "side side-right" }), h("div", { key: '238a3269b2a12d528e8a1889d3109ee319c7fd88', class: "side side-bottom" }), h("div", { key: 'b02eec6c102d7e9fb03ef24dfce7c75e081ff1c4', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '15588129ca8027a6ec1c81d70001142986966826', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'f15fd9bddc20c54afcca7be79c3cf2f9ee2b74f7', class: "back-capture-section" }, h("div", { key: 'e3284e1301840a9555857fa879de9f4ca91dc21b', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: '8508d13e117deb3b223308497b1db976643b86f2', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: '99bd464541d9ee8589bc11e937c5ad6bca9d95c8', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: '3de5f0a142f9dfdfe4578c0fcbe004bb5fe94c56', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '268ce949bf5155b3729298f23eea0d7ec15b3590', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
19748
+ } })))), 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
19711
19749
  ? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
19712
- : 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'bb5c00ad882c303d8a2b6ddc79c9d5b27ff9824e', class: "camera-controls" }, h("button", { key: '7d231b550b58a8336f125a1a7fe7235c8bb3d7dd', 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: '95d1e0d0937f7ef821ff3ad167cd42e25fd1c87f', class: "camera-selector-dropdown" }, h("div", { key: 'e42219e5895c82f40c559c27c4f80f3145fc0a0e', class: "camera-selector-header" }, h("span", { key: '544a6fb2e08211ab6c44b0bfb281124bee318fae' }, "Seleccionar C\u00E1mara"), h("button", { key: '7c4a71243278491ee7dbcc7ab2564bc7e92f5da3', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'c9d24e0bd906d20370214fdfd24ae91abfda98e1', 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: '8c2e1d2ed02bc55c84f3f726e5be577686d0f32d', class: "device-info" }, h("small", { key: '142410a86e4f819b96ffc9573816994351588a3e' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '48c33b2d6c8c80d18fba6e5f454faaa24c28fbd9', class: "manual-capture-section" }, h("button", { key: 'e3d5bfe9ef8b8f16bf669b66bce4e7a500113798', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: 'f50640fe4308c765011e6ed175528920228df5e7', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '080ab315293fa1fe5b1d97aa2511a2ffe2b013f4', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'b062c546af50b799bb1fb7dda07b3ffa601c4bac', class: "flip-animation" }, h("div", { key: 'f7162dc31673c7db1d8e03a658a722a3d323b28e', class: "id-card-icon" }), h("div", { key: 'd950952b480342d80d37df5c48031d72be4ac01a', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'bd576fd28065aa712e42636c5fd0e3566d26cc25', class: "success-animation" }, h("div", { key: '6ca5c24510df790bd1c955c5e2936909c4767560', class: "check-icon" }), h("div", { key: '3420164eacddbe1a9aa0a181528ba0be7683af56', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '8ec2b295b233e7bb8ab0cc1c183adedd4e3c7e04', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '97c8e7e6219984673ebc631da111c17bf72a83cb', class: "status-spinner" })), h("div", { key: '7ca06c48f295892ff77eceff6245891ef7d9dd5e', class: "status-content" }, h("div", { key: '08056c7ec8fd66d23e0b6714c08aeccc092cf46a', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '1e32ca14de53cc2f105822ed5a58e691918b307b', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'c25022d9aadd37ead9ae7d1cf7c7d916cdeb9cc1', class: "performance-monitor" }, h("div", { key: 'f0f99c7e820532432ba64726ef0c2aeabeb3309f', class: "performance-expanded" }, h("div", { key: 'e839e90c9cb0f33068ac3311e41914aa52610149', class: "metrics-row" }, h("div", { key: '9f74f4877a048e63f542f2df54f569f79d6f8bd0', class: "metric-compact" }, h("span", { key: '21ec7255706d6fb2cfec035c4be92295ee0216b6', class: "metric-label" }, "FPS"), h("span", { key: 'c8be17f3dc752304bf6b7fd9746f11783f7ff6ca', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '79187257ea30ead7fb41534a94272a3a855e050b', class: "metric-compact" }, h("span", { key: 'cc53dc282c1fe4234217207c2085fa2be06a5458', class: "metric-label" }, "MEM"), h("span", { key: '032002ff63b3a2349b34833668fed8d6337e2063', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: 'e628febd5bbfb9f593e029b10827635ddd0344fb', class: "metrics-row" }, h("div", { key: 'b2fe558bab709ff6b6781648b13ebc66238f3298', class: "metric-compact" }, h("span", { key: 'd7d816aff11503657781f8ebe9cb5f0f07c7279e', class: "metric-label" }, "INF"), h("span", { key: '3d735d092b5b39d0e96f4ac59b3bf2acde739fab', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '9a83fd4fe43e8a3760aaea1f134c3101b8757681', class: "metric-compact" }, h("span", { key: '8136ab82b50230537f19cd58ed2da0956d291287', class: "metric-label" }, "FRAME"), h("span", { key: '6221cc10edff8c4b0e4034a3734deb548416a3e6', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: 'a31bad44d5c2c5fd5417ed82589c0f103bba1d32', class: "metrics-row" }, h("div", { key: '1441d11e0d83b3b44dfa963e415c3074691eb4f2', class: "metric-compact" }, h("span", { key: '9fe5643129aa7a842f75644b6f71b1ff80993f1b', class: "metric-label" }, "DET"), h("span", { key: '0604065a3867e71a745bfb4e804c7e96048cb70e', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'daff0fede819f2b59a4ddb83560dc53f6151c2c9', class: "metric-compact" }, h("span", { key: '75a55c4a4e36b98af3620866ec6f7842b9ef81f7', class: "metric-label" }, "RATE"), h("span", { key: '61776c3fd4c3200d5e18cc28d917c538b282625a', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '4092c684ee3a148ec40e266fd16a718f2c2f2f52', class: "watermark" }, h("img", { key: 'd7bae6157d22675f8b71dbf1afdf345374398c82', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" }))))));
19750
+ : '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" }))))));
19713
19751
  }
19714
19752
  // Utility methods
19715
19753
  updateDetectionBoxes(boxes) {
@@ -20432,28 +20470,35 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
20432
20470
  }
20433
20471
  // Adaptive frame skipping based on performance
20434
20472
  getAdaptiveFrameSkip() {
20473
+ // Check if running on mobile device for more aggressive frame skipping
20474
+ const isMobileDevice = this.cameraInfoWithAutofocus.deviceType === 'mobile' ||
20475
+ /Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
20476
+ const isTablet = this.cameraInfoWithAutofocus.deviceType === 'tablet' ||
20477
+ (/iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768);
20478
+ // Mobile-specific base frame skip (more aggressive to prevent slow motion effect)
20479
+ const mobileBaseSkip = isMobileDevice && !isTablet ? 4 : this.BASE_FRAME_SKIP;
20435
20480
  // Base conditions for high frame skip
20436
20481
  if (this.consecutiveFailures > 20)
20437
20482
  return this.MAX_FRAME_SKIP;
20438
20483
  if (this.performanceMetrics.inferenceTime > 200)
20439
- return 6;
20484
+ return Math.max(6, mobileBaseSkip);
20440
20485
  if (this.performanceMetrics.memoryUsage > 200)
20441
- return 5;
20486
+ return Math.max(5, mobileBaseSkip);
20442
20487
  // Calculate average processing time
20443
20488
  if (this.performanceHistory.length > 0) {
20444
20489
  const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
20445
20490
  if (avgProcessingTime > 100)
20446
- return 4;
20491
+ return Math.max(4, mobileBaseSkip);
20447
20492
  if (avgProcessingTime > 80)
20448
- return 3;
20493
+ return Math.max(3, mobileBaseSkip);
20449
20494
  if (avgProcessingTime > 60)
20450
- return this.BASE_FRAME_SKIP + 1;
20495
+ return Math.max(this.BASE_FRAME_SKIP + 1, mobileBaseSkip);
20451
20496
  }
20452
20497
  // Low memory devices get higher frame skip
20453
20498
  if (this.performanceMetrics.memoryUsage > 150)
20454
- return 4;
20455
- // Performance is good, use base frame skip
20456
- return this.BASE_FRAME_SKIP;
20499
+ return Math.max(4, mobileBaseSkip);
20500
+ // Mobile devices use higher base frame skip for smoother video
20501
+ return mobileBaseSkip;
20457
20502
  }
20458
20503
  // Método para resetear la máscara a color blanco
20459
20504
  resetMaskToWhite() {