{"version":3,"file":"device-detection-lQZK_7ak.cjs","sources":["../../lib/vowel/utils/device-detection.ts"],"sourcesContent":["/**\n * @fileoverview Device Detection - Utilities for detecting device capabilities\n * \n * Provides utilities for detecting device type, screen size, touch support,\n * and other device-specific features to enable adaptive UI.\n * \n * @module @vowel.to/client/utils\n * @author vowel.to\n * @license Proprietary\n */\n\n/**\n * Device type classification\n */\nexport type DeviceType = 'mobile' | 'tablet' | 'desktop';\n\n/**\n * Device capabilities\n */\nexport interface DeviceCapabilities {\n  /** Device type (mobile, tablet, desktop) */\n  type: DeviceType;\n  \n  /** Whether the device has touch support */\n  hasTouch: boolean;\n  \n  /** Screen width in pixels */\n  screenWidth: number;\n  \n  /** Screen height in pixels */\n  screenHeight: number;\n  \n  /** Device pixel ratio */\n  pixelRatio: number;\n  \n  /** Whether the device is in portrait orientation */\n  isPortrait: boolean;\n  \n  /** Whether the device is in landscape orientation */\n  isLandscape: boolean;\n  \n  /** Whether the browser is in standalone/PWA mode */\n  isStandalone: boolean;\n  \n  /** User agent string */\n  userAgent: string;\n  \n  /** Whether running on iOS */\n  isIOS: boolean;\n  \n  /** Whether running on Android */\n  isAndroid: boolean;\n  \n  /** Whether running on macOS */\n  isMacOS: boolean;\n  \n  /** Whether running on Windows */\n  isWindows: boolean;\n}\n\n/**\n * Detect device type based on screen width and user agent\n */\nexport function getDeviceType(): DeviceType {\n  if (typeof window === 'undefined') {\n    return 'desktop';\n  }\n\n  const width = window.innerWidth;\n  const userAgent = navigator.userAgent.toLowerCase();\n\n  // Check user agent for explicit mobile/tablet indicators\n  const isMobileUA = /mobile|android|iphone|ipod|blackberry|iemobile|opera mini/i.test(userAgent);\n  const isTabletUA = /tablet|ipad|playbook|silk|kindle/i.test(userAgent);\n\n  // Use breakpoints (following common standards)\n  if (width < 768) {\n    return 'mobile';\n  } else if (width >= 768 && width < 1024) {\n    return isTabletUA || isMobileUA ? 'tablet' : 'desktop';\n  } else {\n    return isTabletUA ? 'tablet' : 'desktop';\n  }\n}\n\n/**\n * Detect if device has touch support\n */\nexport function hasTouchSupport(): boolean {\n  if (typeof window === 'undefined') {\n    return false;\n  }\n\n  return (\n    'ontouchstart' in window ||\n    navigator.maxTouchPoints > 0 ||\n    (navigator as any).msMaxTouchPoints > 0\n  );\n}\n\n/**\n * Detect device orientation\n */\nexport function getOrientation(): 'portrait' | 'landscape' {\n  if (typeof window === 'undefined') {\n    return 'landscape';\n  }\n\n  return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';\n}\n\n/**\n * Detect if running in standalone/PWA mode\n */\nexport function isStandalone(): boolean {\n  if (typeof window === 'undefined') {\n    return false;\n  }\n\n  return (\n    ('standalone' in window.navigator && (window.navigator as any).standalone) ||\n    window.matchMedia('(display-mode: standalone)').matches\n  );\n}\n\n/**\n * Detect operating system\n */\nexport function getOperatingSystem(): string {\n  if (typeof window === 'undefined') {\n    return 'unknown';\n  }\n\n  const userAgent = navigator.userAgent.toLowerCase();\n\n  if (/iphone|ipad|ipod/.test(userAgent)) {\n    return 'ios';\n  } else if (/android/.test(userAgent)) {\n    return 'android';\n  } else if (/mac os x/.test(userAgent)) {\n    return 'macos';\n  } else if (/windows/.test(userAgent)) {\n    return 'windows';\n  } else if (/linux/.test(userAgent)) {\n    return 'linux';\n  }\n\n  return 'unknown';\n}\n\n/**\n * Get comprehensive device capabilities\n */\nexport function getDeviceCapabilities(): DeviceCapabilities {\n  if (typeof window === 'undefined') {\n    return {\n      type: 'desktop',\n      hasTouch: false,\n      screenWidth: 1920,\n      screenHeight: 1080,\n      pixelRatio: 1,\n      isPortrait: false,\n      isLandscape: true,\n      isStandalone: false,\n      userAgent: '',\n      isIOS: false,\n      isAndroid: false,\n      isMacOS: false,\n      isWindows: false,\n    };\n  }\n\n  const orientation = getOrientation();\n  const os = getOperatingSystem();\n\n  return {\n    type: getDeviceType(),\n    hasTouch: hasTouchSupport(),\n    screenWidth: window.innerWidth,\n    screenHeight: window.innerHeight,\n    pixelRatio: window.devicePixelRatio || 1,\n    isPortrait: orientation === 'portrait',\n    isLandscape: orientation === 'landscape',\n    isStandalone: isStandalone(),\n    userAgent: navigator.userAgent,\n    isIOS: os === 'ios',\n    isAndroid: os === 'android',\n    isMacOS: os === 'macos',\n    isWindows: os === 'windows',\n  };\n}\n\n/**\n * Check if device is mobile (phone)\n */\nexport function isMobile(): boolean {\n  return getDeviceType() === 'mobile';\n}\n\n/**\n * Check if device is tablet\n */\nexport function isTablet(): boolean {\n  return getDeviceType() === 'tablet';\n}\n\n/**\n * Check if device is desktop\n */\nexport function isDesktop(): boolean {\n  return getDeviceType() === 'desktop';\n}\n\n/**\n * Check if device is mobile or tablet (any touch device)\n */\nexport function isMobileOrTablet(): boolean {\n  const type = getDeviceType();\n  return type === 'mobile' || type === 'tablet';\n}\n\n/**\n * Listen for device capability changes (orientation, resize)\n */\nexport function onDeviceChange(callback: (capabilities: DeviceCapabilities) => void): () => void {\n  if (typeof window === 'undefined') {\n    return () => {};\n  }\n\n  const handler = () => {\n    callback(getDeviceCapabilities());\n  };\n\n  window.addEventListener('resize', handler);\n  window.addEventListener('orientationchange', handler);\n\n  // Call immediately\n  handler();\n\n  // Return cleanup function\n  return () => {\n    window.removeEventListener('resize', handler);\n    window.removeEventListener('orientationchange', handler);\n  };\n}\n\n/**\n * Get safe area insets (for devices with notches, etc.)\n */\nexport function getSafeAreaInsets(): {\n  top: number;\n  right: number;\n  bottom: number;\n  left: number;\n} {\n  if (typeof window === 'undefined' || typeof getComputedStyle === 'undefined') {\n    return { top: 0, right: 0, bottom: 0, left: 0 };\n  }\n\n  const style = getComputedStyle(document.documentElement);\n\n  return {\n    top: parseInt(style.getPropertyValue('--sat') || style.getPropertyValue('env(safe-area-inset-top)') || '0', 10),\n    right: parseInt(style.getPropertyValue('--sar') || style.getPropertyValue('env(safe-area-inset-right)') || '0', 10),\n    bottom: parseInt(style.getPropertyValue('--sab') || style.getPropertyValue('env(safe-area-inset-bottom)') || '0', 10),\n    left: parseInt(style.getPropertyValue('--sal') || style.getPropertyValue('env(safe-area-inset-left)') || '0', 10),\n  };\n}\n\n/**\n * Check if device prefers reduced motion\n */\nexport function prefersReducedMotion(): boolean {\n  if (typeof window === 'undefined') {\n    return false;\n  }\n\n  return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/**\n * Check if device prefers dark mode\n */\nexport function prefersDarkMode(): boolean {\n  if (typeof window === 'undefined') {\n    return false;\n  }\n\n  return window.matchMedia('(prefers-color-scheme: dark)').matches;\n}\n\n\n\n\n"],"names":[],"mappings":";;AA+DO,SAAS,aAAA,GAA4B;AAC1C,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,SAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAQ,MAAA,CAAO,UAAA;AACrB,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,SAAA,CAAU,WAAA,EAAY;AAGlD,EAAA,MAAM,UAAA,GAAa,4DAAA,CAA6D,IAAA,CAAK,SAAS,CAAA;AAC9F,EAAA,MAAM,UAAA,GAAa,mCAAA,CAAoC,IAAA,CAAK,SAAS,CAAA;AAGrE,EAAA,IAAI,QAAQ,GAAA,EAAK;AACf,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAA,IAAW,KAAA,IAAS,GAAA,IAAO,KAAA,GAAQ,IAAA,EAAM;AACvC,IAAA,OAAO,UAAA,IAAc,aAAa,QAAA,GAAW,SAAA;AAAA,EAC/C,CAAA,MAAO;AACL,IAAA,OAAO,aAAa,QAAA,GAAW,SAAA;AAAA,EACjC;AACF;AA6CO,SAAS,kBAAA,GAA6B;AAC3C,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,SAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,SAAA,CAAU,WAAA,EAAY;AAElD,EAAA,IAAI,kBAAA,CAAmB,IAAA,CAAK,SAAS,CAAA,EAAG;AACtC,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,MAAA,IAAW,SAAA,CAAU,IAAA,CAAK,SAAS,CAAA,EAAG;AACpC,IAAA,OAAO,SAAA;AAAA,EACT,CAAA,MAAA,IAAW,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA,EAAG;AACrC,IAAA,OAAO,OAAA;AAAA,EACT,CAAA,MAAA,IAAW,SAAA,CAAU,IAAA,CAAK,SAAS,CAAA,EAAG;AACpC,IAAA,OAAO,SAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAA,EAAG;AAClC,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,SAAA;AACT;AA+CO,SAAS,QAAA,GAAoB;AAClC,EAAA,OAAO,eAAc,KAAM,QAAA;AAC7B;AAmBO,SAAS,gBAAA,GAA4B;AAC1C,EAAA,MAAM,OAAO,aAAA,EAAc;AAC3B,EAAA,OAAO,IAAA,KAAS,YAAY,IAAA,KAAS,QAAA;AACvC;;;;;;"}