const LOW_MEMORY_LIMIT_GB = 2 const LOW_CPU_LIMIT = 4 function getNavigator() { if (typeof navigator === 'undefined') { return null } return navigator } function getUserAgent() { const nav = getNavigator() return nav?.userAgent || '' } export function isLowEndDevice() { const nav = getNavigator() if (!nav) { return false } const userAgent = getUserAgent() const deviceMemory = Number(nav.deviceMemory || 0) const hardwareConcurrency = Number(nav.hardwareConcurrency || 0) const isAndroid = /Android/i.test(userAgent) const androidVersionMatch = userAgent.match(/Android\s+(\d+)/i) const androidMajorVersion = androidVersionMatch ? Number(androidVersionMatch[1]) : 0 return ( (deviceMemory > 0 && deviceMemory <= LOW_MEMORY_LIMIT_GB) || (isAndroid && hardwareConcurrency > 0 && hardwareConcurrency <= LOW_CPU_LIMIT) || (isAndroid && androidMajorVersion > 0 && androidMajorVersion <= 9 && hardwareConcurrency > 0 && hardwareConcurrency <= LOW_CPU_LIMIT) ) } export function prefersReducedMotion() { if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { return false } return window.matchMedia('(prefers-reduced-motion: reduce)').matches } export function shouldUseLiteAnimations() { return isLowEndDevice() || prefersReducedMotion() } export function scheduleIdleTask(callback, timeout = 1200) { if (typeof window === 'undefined') { callback() return null } if (typeof window.requestIdleCallback === 'function') { return window.requestIdleCallback(callback, { timeout }) } return window.setTimeout(callback, Math.min(timeout, 300)) } export function cancelIdleTask(taskId) { if (typeof window === 'undefined' || taskId == null) { return } if (typeof window.cancelIdleCallback === 'function') { window.cancelIdleCallback(taskId) return } window.clearTimeout(taskId) } export function applyPerformanceModeClass(target) { const classTarget = target || (typeof document !== 'undefined' ? document.documentElement : null) if (!classTarget) { return } classTarget.classList.toggle('low-performance-mode', isLowEndDevice()) classTarget.classList.toggle('reduce-motion-mode', prefersReducedMotion()) }