Files
sub2api/frontend/src/utils/device.ts
T
J606yandClaude Fable 5 9e072cefec fix(ios): 修复 iOS 端输入框聚焦导致页面自动缩放
iOS Safari 在输入框字号小于 16px 时聚焦会自动放大页面,且失焦后不会恢复,
导致布局错位。启动时仅对 iOS 设备为 viewport 注入 maximum-scale=1 阻止该
行为;iOS 10+ 用户仍可双指手动缩放,Android 不受影响。

- utils/device.ts 新增 detectIOSDevice/isIOSDevice(含 iPadOS 桌面模式识别)
- main.ts 启动阶段执行 initIOSViewportZoomFix
- 补充 detectIOSDevice 单元测试

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:58:46 +08:00

82 lines
2.3 KiB
TypeScript

interface NavigatorUADataLike {
mobile?: boolean
}
interface NavigatorLike {
userAgent?: string
platform?: string
maxTouchPoints?: number
userAgentData?: NavigatorUADataLike
}
interface MediaQueryResultLike {
matches: boolean
}
interface DeviceDetectionEnvironment {
navigator?: NavigatorLike
matchMedia?: (query: string) => MediaQueryResultLike | null | undefined
}
const MOBILE_UA_RE = /\b(Mobi|Android|iPhone|iPod|Windows Phone|webOS|BlackBerry|IEMobile)\b/i
const TABLET_UA_RE = /\b(iPad|Tablet)\b/i
const IOS_UA_RE = /\b(iPhone|iPad|iPod)\b/i
function matchesQuery(
matchMedia: DeviceDetectionEnvironment['matchMedia'],
query: string,
): boolean {
try {
return matchMedia?.(query)?.matches === true
} catch {
return false
}
}
export function detectMobileDevice(env: DeviceDetectionEnvironment = {}): boolean {
const nav = env.navigator
if (!nav) return false
if (nav.userAgentData?.mobile === true) {
return true
}
const userAgent = nav.userAgent || ''
const maxTouchPoints = nav.maxTouchPoints ?? 0
const isIPadOSDesktopMode = nav.platform === 'MacIntel' && maxTouchPoints > 1
const isMobileUA = MOBILE_UA_RE.test(userAgent)
const isTabletUA = TABLET_UA_RE.test(userAgent) || isIPadOSDesktopMode
const coarsePointer = matchesQuery(env.matchMedia, '(pointer: coarse)')
const noHover = matchesQuery(env.matchMedia, '(hover: none)')
const hasTouch = maxTouchPoints > 0
return isMobileUA || isTabletUA || (coarsePointer && noHover && hasTouch)
}
export function isMobileDevice(): boolean {
if (typeof navigator === 'undefined') return false
return detectMobileDevice({
navigator,
matchMedia: typeof window !== 'undefined' ? window.matchMedia.bind(window) : undefined,
})
}
export function detectIOSDevice(env: DeviceDetectionEnvironment = {}): boolean {
const nav = env.navigator
if (!nav) return false
const userAgent = nav.userAgent || ''
const maxTouchPoints = nav.maxTouchPoints ?? 0
// iPadOS 13+ 桌面模式下 UA 伪装成 macOS,需要靠触控点数识别。
const isIPadOSDesktopMode = nav.platform === 'MacIntel' && maxTouchPoints > 1
return IOS_UA_RE.test(userAgent) || isIPadOSDesktopMode
}
export function isIOSDevice(): boolean {
if (typeof navigator === 'undefined') return false
return detectIOSDevice({ navigator })
}