From 8596a6433cf242a755f4676743c2488a3d07786c Mon Sep 17 00:00:00 2001 From: lbb00 Date: Thu, 10 Sep 2026 15:04:08 +0800 Subject: [PATCH 1/3] feat(devices): add release years to presets --- CHANGELOG.md | 4 ++ README.md | 28 +++++++------- README.zh-CN.md | 28 +++++++------- packages/devices/README.md | 16 ++++---- packages/devices/README.zh-CN.md | 13 ++++--- packages/devices/src/devices.ts | 9 +++++ packages/devices/src/index.ts | 1 + packages/devices/src/presets.test.ts | 5 +++ packages/devices/src/presets/android.ts | 7 ++-- packages/devices/src/presets/classic.ts | 6 +-- packages/devices/src/presets/harmony.ts | 7 ++-- packages/devices/src/presets/index.ts | 8 ++-- packages/devices/src/presets/ios.ts | 7 ++-- packages/devices/src/presets/release-year.ts | 37 +++++++++++++++++++ .../devices/src/profile-validation.test.ts | 4 ++ packages/devices/src/validate.ts | 6 +++ 16 files changed, 129 insertions(+), 57 deletions(-) create mode 100644 packages/devices/src/presets/release-year.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 128e5b0..e87b0da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `@devicekit/devices`: bundled profiles now include `releaseYear`, and the preset exports use `PresetDeviceProfile` so selectors can sort models chronologically without handling a missing year. + ## [0.2.3] - 2026-09-08 ### Fixed diff --git a/README.md b/README.md index 573c8c1..7c352d4 100644 --- a/README.md +++ b/README.md @@ -66,20 +66,20 @@ The package READMEs contain the full API references. 171 profiles: 63 iOS, 86 Android, 22 HarmonyOS. Folding phones get one entry per screen. -| Manufacturer | Devices | -| --- | ---: | -| Apple | 63 | -| Samsung | 42 | -| Google | 34 | -| Huawei | 22 | -| Motorola | 3 | -| Microsoft | 2 | -| OnePlus | 2 | -| LG | 1 | -| Nothing | 1 | -| Xiaomi | 1 | - -Manufacturer counts are derived from device names because profiles do not have a manufacturer field. `CLASSIC_DEVICES` contains 19 commonly used profiles for shorter selectors. +| Manufacturer | Profiles | Representative models | +| --- | ---: | --- | +| Apple | 63 | iPhone 17 Pro, iPhone 16 Pro, iPhone X, iPad Pro M4 | +| Samsung | 42 | Galaxy S24 Ultra, Galaxy Z Fold 7, Galaxy Z Flip 7 | +| Google | 34 | Pixel 10 Pro, Pixel 9 Pro Fold, Pixel Tablet | +| Huawei | 22 | HUAWEI Mate 80, HUAWEI Pura 80 Pro, HUAWEI Mate X6 (inner), HUAWEI Mate X6 (outer) | +| Motorola | 3 | Motorola Razr+, Moto G4, Moto G Power | +| Microsoft | 2 | Surface Duo | +| OnePlus | 2 | OnePlus 12, OnePlus Open | +| LG | 1 | LG Optimus L70 | +| Nothing | 1 | Nothing Phone 2 | +| Xiaomi | 1 | Xiaomi 14 | + +Every preset includes a `releaseYear`, so a picker can sort models chronologically. Manufacturer counts are derived from device names because profiles do not have a manufacturer field. `CLASSIC_DEVICES` contains 19 commonly used profiles for shorter selectors. ## Browser and framework support diff --git a/README.zh-CN.md b/README.zh-CN.md index d523fbe..f8ff2ef 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -66,20 +66,20 @@ defineDeviceFrame() 共 171 个机型:iOS 63、Android 86、HarmonyOS 22。折叠机内外屏各算一条。 -| 厂商 | 数量 | -| --- | ---: | -| Apple | 63 | -| Samsung | 42 | -| Google | 34 | -| Huawei | 22 | -| Motorola | 3 | -| Microsoft | 2 | -| OnePlus | 2 | -| LG | 1 | -| Nothing | 1 | -| Xiaomi | 1 | - -机型数据没有厂商字段,因此厂商数量按机型名称统计。`CLASSIC_DEVICES` 另提供 19 个常用机型,适合空间较小的选择器。 +| 厂商 | 数量 | 代表机型 | +| --- | ---: | --- | +| Apple | 63 | iPhone 17 Pro、iPhone 16 Pro、iPhone X、iPad Pro M4 | +| Samsung | 42 | Galaxy S24 Ultra、Galaxy Z Fold 7、Galaxy Z Flip 7 | +| Google | 34 | Pixel 10 Pro、Pixel 9 Pro Fold、Pixel Tablet | +| Huawei | 22 | HUAWEI Mate 80、HUAWEI Pura 80 Pro、HUAWEI Mate X6 (inner)、HUAWEI Mate X6 (outer) | +| Motorola | 3 | Motorola Razr+、Moto G4、Moto G Power | +| Microsoft | 2 | Surface Duo | +| OnePlus | 2 | OnePlus 12、OnePlus Open | +| LG | 1 | LG Optimus L70 | +| Nothing | 1 | Nothing Phone 2 | +| Xiaomi | 1 | Xiaomi 14 | + +每个预设都有 `releaseYear`,机型选择器可以按年份排序。机型数据没有厂商字段,因此厂商数量按机型名称统计。`CLASSIC_DEVICES` 另提供 19 个常用机型,适合空间较小的选择器。 ## 浏览器和框架支持 diff --git a/packages/devices/README.md b/packages/devices/README.md index 5f61d39..7df6786 100644 --- a/packages/devices/README.md +++ b/packages/devices/README.md @@ -61,6 +61,7 @@ const profile = { os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, + releaseYear: 2024, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, @@ -70,7 +71,7 @@ const profile = { } ``` -Only `name`, `os`, `screen`, and `pixelRatio` are required. `screen` uses CSS pixels and is stored in portrait orientation. `orientedScreen()` swaps its dimensions for landscape. +Only `name`, `os`, `screen`, and `pixelRatio` are required. Presets also include `releaseYear`, which is the model's release year and can be used to sort a picker. `screen` uses CSS pixels and is stored in portrait orientation. `orientedScreen()` swaps its dimensions for landscape. Orientation-specific status bars, navigation bars, and safe-area insets are stored separately. The status bar height and the top safe-area inset are separate fields because they can differ on devices with a cutout. `cutout` describes the rendered shape; safe-area fields describe the space available to content. @@ -120,12 +121,12 @@ const version = systemVersion(profile) | Export | Type | Description | | --- | --- | --- | -| `DEVICES` | `readonly DeviceProfile[]` | Complete table in iOS, Android, HarmonyOS order | -| `IOS_DEVICES` | `readonly DeviceProfile[]` | iOS profiles | -| `ANDROID_DEVICES` | `readonly DeviceProfile[]` | Android profiles | -| `HARMONY_DEVICES` | `readonly DeviceProfile[]` | HarmonyOS profiles | -| `CLASSIC_DEVICES` | `readonly DeviceProfile[]` | 19 profiles for short selectors | -| `DEFAULT_DEVICE` | `DeviceProfile` | iPhone X profile | +| `DEVICES` | `readonly PresetDeviceProfile[]` | Complete table in iOS, Android, HarmonyOS order | +| `IOS_DEVICES` | `readonly PresetDeviceProfile[]` | iOS profiles | +| `ANDROID_DEVICES` | `readonly PresetDeviceProfile[]` | Android profiles | +| `HARMONY_DEVICES` | `readonly PresetDeviceProfile[]` | HarmonyOS profiles | +| `CLASSIC_DEVICES` | `readonly PresetDeviceProfile[]` | 19 profiles for short selectors | +| `DEFAULT_DEVICE` | `PresetDeviceProfile` | iPhone X profile | | `PLATFORM_DEFAULTS` | `Record` | Default bar and shell values by platform | | `DEVICE_NAMES` | `const object` | Typed name constants generated from `DEVICES` | @@ -154,6 +155,7 @@ const version = systemVersion(profile) | Type | Description | | --- | --- | | `DeviceProfile` | A stored profile with optional device-specific fields | +| `PresetDeviceProfile` | A bundled profile with a required `releaseYear` | | `ResolvedDevice` | A profile with defaults applied | | `DeviceOS` | `'ios' \| 'android' \| 'harmony'` | | `DeviceFormFactor` | `'phone' \| 'tablet'` | diff --git a/packages/devices/README.zh-CN.md b/packages/devices/README.zh-CN.md index e5529f4..6a7ac97 100644 --- a/packages/devices/README.zh-CN.md +++ b/packages/devices/README.zh-CN.md @@ -36,6 +36,7 @@ const device = findDevice(DEVICE_NAMES.iPhone_16_Pro) ?? DEFAULT_DEVICE os: 'ios', screen: { width: 402, height: 874 }, // 物理屏,竖屏方向 pixelRatio: 3, + releaseYear: 2024, // 机型发布年份 system: 'iOS 18.5', statusBarHeight: 54, // 画出来的那条状态栏 safeAreaInsets: { top: 62, bottom: 34 }, // 竖屏,实测值 @@ -149,12 +150,12 @@ import { deviceUserAgent, systemVersion } from '@devicekit/devices' | 导出 | 类型 | 是什么 | | --- | --- | --- | -| `DEVICES` | `readonly DeviceProfile[]` | 整张表,iOS、Android、HarmonyOS 顺序 | -| `IOS_DEVICES` | `readonly DeviceProfile[]` | iOS 那部分 | -| `ANDROID_DEVICES` | `readonly DeviceProfile[]` | Android 那部分 | -| `HARMONY_DEVICES` | `readonly DeviceProfile[]` | HarmonyOS 那部分 | -| `CLASSIC_DEVICES` | `readonly DeviceProfile[]` | 手选的不到 20 台,同一批对象,给短列表用 | -| `DEFAULT_DEVICE` | `DeviceProfile` | 没指定机型时画的那台(iPhone X) | +| `DEVICES` | `readonly PresetDeviceProfile[]` | 整张表,iOS、Android、HarmonyOS 顺序 | +| `IOS_DEVICES` | `readonly PresetDeviceProfile[]` | iOS 那部分 | +| `ANDROID_DEVICES` | `readonly PresetDeviceProfile[]` | Android 那部分 | +| `HARMONY_DEVICES` | `readonly PresetDeviceProfile[]` | HarmonyOS 那部分 | +| `CLASSIC_DEVICES` | `readonly PresetDeviceProfile[]` | 手选的不到 20 台,同一批对象,给短列表用 | +| `DEFAULT_DEVICE` | `PresetDeviceProfile` | 没指定机型时画的那台(iPhone X) | | `PLATFORM_DEFAULTS` | `Record` | 各平台的状态栏、导航栏和机身默认值 | | `DEVICE_NAMES` | `{ [key: string]: string }`(as const) | 每个 `DEVICES[number].name`,键是 `deviceNameKey(name)`——生成文件,见下 | diff --git a/packages/devices/src/devices.ts b/packages/devices/src/devices.ts index f494563..7f15043 100644 --- a/packages/devices/src/devices.ts +++ b/packages/devices/src/devices.ts @@ -118,6 +118,8 @@ export interface DeviceProfile { formFactor?: DeviceFormFactor /** Shown in the device picker, e.g. "iOS 18.0". Also feeds the generated user agent. */ system?: string + /** Calendar year in which this model was released. */ + releaseYear?: number /** * What a page emulating this device should report as `navigator.userAgent`. * Omitted = generated from `os` and `system` — see deviceUserAgent(). @@ -148,6 +150,11 @@ export interface DeviceProfile { shell?: Partial } +/** A bundled profile, whose release year is known and always present. */ +export interface PresetDeviceProfile extends DeviceProfile { + releaseYear: number +} + /** * Fallbacks for a profile that leaves a field out, and for hosts that only know * the platform. @@ -200,6 +207,7 @@ export interface ResolvedDevice { pixelRatio: number formFactor: DeviceFormFactor system: string + releaseYear?: number userAgent: string statusBarHeight: number statusBarHeightLandscape: number @@ -254,6 +262,7 @@ export function resolveDevice(profile: DeviceProfile): ResolvedDevice { pixelRatio: profile.pixelRatio, formFactor: profile.formFactor ?? 'phone', system: profile.system ?? '', + ...(profile.releaseYear === undefined ? {} : { releaseYear: profile.releaseYear }), userAgent: profile.userAgent ?? deviceUserAgent(profile), statusBarHeight, statusBarHeightLandscape, diff --git a/packages/devices/src/index.ts b/packages/devices/src/index.ts index 937e7f0..6a7b05e 100644 --- a/packages/devices/src/index.ts +++ b/packages/devices/src/index.ts @@ -9,6 +9,7 @@ export { type DeviceFormFactor, type DeviceOS, type DeviceProfile, + type PresetDeviceProfile, type DeviceShell, type EdgeInsets, type HomeButtonSpec, diff --git a/packages/devices/src/presets.test.ts b/packages/devices/src/presets.test.ts index 7a29b8b..a35765d 100644 --- a/packages/devices/src/presets.test.ts +++ b/packages/devices/src/presets.test.ts @@ -41,6 +41,11 @@ describe('the table is well formed', () => { expect(broken.map((d) => d.name)).toEqual([]) }) + it('records the release year for every device so consumers can sort the table chronologically', () => { + const missingOrInvalid = DEVICES.filter((d) => !Number.isInteger(d.releaseYear) || d.releaseYear! < 2000 || d.releaseYear! > 2100) + expect(missingOrInvalid.map((d) => d.name)).toEqual([]) + }) + // Rotating is what the orientation argument is for. A source table that also // ships a pre-rotated copy of a device leaves two rows here where one turns // into the other, and the pre-rotated one then rotates backwards. diff --git a/packages/devices/src/presets/android.ts b/packages/devices/src/presets/android.ts index ff48f6e..db56f81 100644 --- a/packages/devices/src/presets/android.ts +++ b/packages/devices/src/presets/android.ts @@ -4,10 +4,11 @@ * One line per device. Omitted fields fall back to the platform defaults in * devices.ts — see the README for what each source could and could not tell us. */ -import type { DeviceProfile } from '../devices.js' +import type { PresetDeviceProfile } from '../devices.js' +import { withReleaseYears } from './release-year.js' /** Android phones and tablets. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const ANDROID_DEVICES: readonly DeviceProfile[] = [ +export const ANDROID_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ { name: 'Pixel 7', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', statusBarHeight: 52, safeAreaInsets: { top: 52 }, safeAreaInsetsLandscape: { right: 52 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, { name: 'Pixel 8', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, { name: 'Pixel 8 Pro', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, @@ -96,4 +97,4 @@ export const ANDROID_DEVICES: readonly DeviceProfile[] = [ { name: 'Xiaomi 14', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; 23127PN0CC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, { name: 'Motorola Razr+', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; XT2321-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, { name: 'OnePlus Open', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; CPH2551) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, -] +]) diff --git a/packages/devices/src/presets/classic.ts b/packages/devices/src/presets/classic.ts index b941daa..7885efd 100644 --- a/packages/devices/src/presets/classic.ts +++ b/packages/devices/src/presets/classic.ts @@ -1,4 +1,4 @@ -import type { DeviceProfile } from '../devices.js' +import type { PresetDeviceProfile } from '../devices.js' import { ANDROID_DEVICES } from './android.js' import { HARMONY_DEVICES } from './harmony.js' import { IOS_DEVICES } from './ios.js' @@ -7,7 +7,7 @@ import { IOS_DEVICES } from './ios.js' // DEVICES in ./index.js, so this module has no circular dependency on it. const BY_NAME = new Map([...IOS_DEVICES, ...ANDROID_DEVICES, ...HARMONY_DEVICES].map((device) => [device.name, device])) -function pick(name: string): DeviceProfile { +function pick(name: string): PresetDeviceProfile { const device = BY_NAME.get(name) if (!device) throw new Error(`CLASSIC_DEVICES: no device named ${JSON.stringify(name)} in DEVICES`) return device @@ -19,7 +19,7 @@ function pick(name: string): DeviceProfile { * objects as in the full table (never copies), grouped iOS → Android → * HarmonyOS so a grouped dropdown needs no sorting of its own. */ -export const CLASSIC_DEVICES: readonly DeviceProfile[] = [ +export const CLASSIC_DEVICES: readonly PresetDeviceProfile[] = [ pick('iPhone SE (3rd gen)'), pick('iPhone X'), pick('iPhone 12/13 (Pro)'), diff --git a/packages/devices/src/presets/harmony.ts b/packages/devices/src/presets/harmony.ts index c39588b..acbcda0 100644 --- a/packages/devices/src/presets/harmony.ts +++ b/packages/devices/src/presets/harmony.ts @@ -4,10 +4,11 @@ * One line per device. Omitted fields fall back to the platform defaults in * devices.ts — see the README for what each source could and could not tell us. */ -import type { DeviceProfile } from '../devices.js' +import type { PresetDeviceProfile } from '../devices.js' +import { withReleaseYears } from './release-year.js' /** HarmonyOS phones, including both screens of each folding model. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const HARMONY_DEVICES: readonly DeviceProfile[] = [ +export const HARMONY_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ { name: 'HUAWEI Mate 80', os: 'harmony', screen: { width: 366, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, { name: 'HUAWEI Mate 70', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 } }, { name: 'HUAWEI Mate 70 Pro', os: 'harmony', screen: { width: 376, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, @@ -31,4 +32,4 @@ export const HARMONY_DEVICES: readonly DeviceProfile[] = [ { name: 'HUAWEI Pura X Max (outer)', os: 'harmony', screen: { width: 460, height: 672 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 66, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 66 } }, // Table stores portrait; the unfolded (wider-than-tall) screen is what orientation=landscape reads out. { name: 'HUAWEI Pura X Max (inner)', os: 'harmony', screen: { width: 665, height: 940 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 72, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 72 } }, -] +]) diff --git a/packages/devices/src/presets/index.ts b/packages/devices/src/presets/index.ts index 8fe92e7..8d3962d 100644 --- a/packages/devices/src/presets/index.ts +++ b/packages/devices/src/presets/index.ts @@ -32,7 +32,7 @@ * device means adding a line; presets.test.ts holds the invariants that every * row has to satisfy. */ -import type { DeviceProfile } from '../devices.js' +import type { PresetDeviceProfile } from '../devices.js' import { ANDROID_DEVICES } from './android.js' import { HARMONY_DEVICES } from './harmony.js' import { IOS_DEVICES } from './ios.js' @@ -40,12 +40,12 @@ import { IOS_DEVICES } from './ios.js' export { ANDROID_DEVICES, HARMONY_DEVICES, IOS_DEVICES } /** Every device in the table, ordered iOS → Android → HarmonyOS. */ -export const DEVICES: readonly DeviceProfile[] = [...IOS_DEVICES, ...ANDROID_DEVICES, ...HARMONY_DEVICES] +export const DEVICES: readonly PresetDeviceProfile[] = [...IOS_DEVICES, ...ANDROID_DEVICES, ...HARMONY_DEVICES] const BY_NAME = new Map(DEVICES.map((device) => [device.name, device])) /** What renders when nothing asked for a particular device. */ -export const DEFAULT_DEVICE: DeviceProfile = BY_NAME.get('iPhone X')! +export const DEFAULT_DEVICE: PresetDeviceProfile = BY_NAME.get('iPhone X')! /** * Looks a device up by its `name`, which is unique across the whole table. @@ -54,7 +54,7 @@ export const DEFAULT_DEVICE: DeviceProfile = BY_NAME.get('iPhone X')! * caller can pass a missing attribute straight through * @returns the table's own object, or undefined when no row has that name */ -export function findDevice(name: string | null | undefined): DeviceProfile | undefined { +export function findDevice(name: string | null | undefined): PresetDeviceProfile | undefined { if (!name) return undefined return BY_NAME.get(name) } diff --git a/packages/devices/src/presets/ios.ts b/packages/devices/src/presets/ios.ts index 29c354e..23ee5cc 100644 --- a/packages/devices/src/presets/ios.ts +++ b/packages/devices/src/presets/ios.ts @@ -4,12 +4,13 @@ * One line per device. Omitted fields fall back to the platform defaults in * devices.ts — see the README for what each source could and could not tell us. */ -import type { DeviceProfile } from '../devices.js' +import type { PresetDeviceProfile } from '../devices.js' +import { withReleaseYears } from './release-year.js' const LEGACY_IPHONE_SHELL = { screenRadius: 0, bodyRadius: 38, bezelInsets: { top: 44, bottom: 48 }, homeButton: { diameter: 30 } } /** iPhones and iPads. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const IOS_DEVICES: readonly DeviceProfile[] = [ +export const IOS_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ { name: 'iPhone SE', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 15.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, { name: 'iPhone XR', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 } }, { name: 'iPhone 12 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 } }, @@ -73,4 +74,4 @@ export const IOS_DEVICES: readonly DeviceProfile[] = [ { name: 'iPhone 17 Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.0', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 } }, { name: 'iPad Pro M4', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1210 }, pixelRatio: 2, system: 'iOS 17.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, { name: 'iPad Air M2', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 17.4', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, -] +]) diff --git a/packages/devices/src/presets/release-year.ts b/packages/devices/src/presets/release-year.ts new file mode 100644 index 0000000..068755f --- /dev/null +++ b/packages/devices/src/presets/release-year.ts @@ -0,0 +1,37 @@ +import type { DeviceProfile, PresetDeviceProfile } from '../devices.js' + +const byYear = (year: number, names: readonly string[]): Record => + Object.fromEntries(names.map((name) => [name, year])) + +/** + * Calendar year in which the model represented by each preset was released. + * Names that intentionally group several generations use the earliest model + * in that name, keeping chronological sorting deterministic without claiming + * that the grouped models were released together. + */ +const RELEASE_YEAR_BY_NAME: Readonly> = { + ...byYear(2010, ['iPhone 4']), + ...byYear(2012, ['iPhone 5', 'iPhone 5/SE', 'Galaxy Note II', 'Galaxy S III', 'Nexus 4', 'Nexus 7', 'Nexus 10']), + ...byYear(2013, ['Nexus 5', 'Galaxy Note 3']), + ...byYear(2014, ['iPhone 6', 'iPhone 6 Plus', 'iPhone 6/7/8', 'iPhone 6/7/8 Plus', 'LG Optimus L70', 'Galaxy S5', 'Nexus 6']), + ...byYear(2015, ['Nexus 5X', 'Nexus 6P', 'iPad Pro 12.9-inch']), + ...byYear(2016, ['iPhone 7', 'iPhone 7 Plus', 'Moto G4']), + ...byYear(2017, ['iPhone 8', 'iPhone 8 Plus', 'iPhone X', 'iPad', 'iPad Pro 10.5-inch', 'iPad (gen 5)', 'Samsung Galaxy S8+', 'Pixel 2', 'Pixel 2 XL']), + ...byYear(2018, ['iPhone XR', 'iPhone XS Max', 'iPad (gen 6)', 'iPad Pro 11', 'iPad Pro', 'Galaxy S9+', 'Galaxy Tab S4', 'Pixel 3', 'Pixel 3 XL']), + ...byYear(2019, ['iPhone 11', 'iPhone 11 Pro', 'iPhone 11 Pro Max', 'iPad (gen 7)', 'Pixel 4', 'Samsung Galaxy A51/71', 'Galaxy Fold']), + ...byYear(2020, ['iPhone SE', 'iPhone 12', 'iPhone 12 Pro', 'iPhone 12 Pro Max', 'iPhone 12 Mini', 'iPhone 12/13 mini', 'iPhone 12/13 (Pro)', 'iPhone 12/13 Pro Max', 'iPad Air', 'Samsung Galaxy S20 Ultra', 'Surface Duo', 'Surface Duo (inner)', 'Galaxy S20', 'Galaxy S20 Plus', 'Pixel 4a (5G)', 'Pixel 5']), + ...byYear(2021, ['iPhone 13', 'iPhone 13 Pro', 'iPhone 13 Pro Max', 'iPhone 13 Mini', 'iPad Mini', 'Moto G Power', 'Galaxy S21', 'Galaxy S21 Plus', 'Galaxy S21 Ultra', 'Samsung S21 FE', 'Galaxy Fold3 (Folded)', 'Galaxy Fold3 (Unfolded)', 'Pixel 6', 'Pixel 6 Pro']), + ...byYear(2022, ['iPhone 14', 'iPhone 14 Plus', 'iPhone 14 Pro', 'iPhone 14 Pro Max', 'iPhone SE (3rd gen)', 'Galaxy S22', 'Galaxy S22 Plus', 'Galaxy S22 Ultra', 'Pixel 6a', 'Pixel 7', 'Pixel 7 Pro', 'Galaxy Z Fold 4']), + ...byYear(2023, ['iPhone 15', 'iPhone 15 Plus', 'iPhone 15 Pro', 'iPhone 15 Pro Max', 'Galaxy Tab S9', 'Galaxy S23', 'Galaxy S23 Plus', 'Galaxy S23 Ultra', 'Galaxy Z Fold 5', 'Galaxy Z Fold 5 (inner)', 'Galaxy Z Flip 5', 'Pixel 7a', 'Pixel 8', 'Pixel 8 Pro', 'Pixel Fold', 'Pixel Tablet', 'Nothing Phone 2', 'Motorola Razr+', 'OnePlus Open', 'HUAWEI Mate 60', 'HUAWEI Mate 60 Pro', 'HUAWEI Mate X5 (inner)', 'HUAWEI Mate X5 (outer)', 'HUAWEI nova 12', 'HUAWEI nova 12 Pro']), + ...byYear(2024, ['iPhone 16', 'iPhone 16 Plus', 'iPhone 16 Pro', 'iPhone 16 Pro Max', 'iPad Pro 13', 'iPad Pro M4', 'iPad Air M2', 'Samsung Galaxy A55', 'Pixel 8a', 'Pixel 9', 'Pixel 9 Pro', 'Pixel 9 Pro XL', 'Pixel 9 Pro Fold', 'Pixel 9 Pro Fold (inner)', 'Galaxy S24', 'Galaxy S24 Plus', 'Galaxy S24 Ultra', 'Galaxy Z Fold 6', 'Galaxy Z Fold 6 (inner)', 'Galaxy Z Fold 6 Cover', 'Galaxy Z Flip 6', 'Galaxy Z Flip 6 Cover', 'OnePlus 12', 'Xiaomi 14', 'HUAWEI Mate 70', 'HUAWEI Mate 70 Pro', 'HUAWEI Mate X6 (inner)', 'HUAWEI Mate X6 (outer)', 'HUAWEI Pura 70', 'HUAWEI Pura 70 Pro', 'HUAWEI Pocket 2', 'HUAWEI nova 13', 'HUAWEI nova 13 Pro']), + ...byYear(2025, ['iPhone 16e', 'iPad (gen 11)', 'iPhone 17', 'iPhone Air', 'iPhone 17 Air', 'iPhone 17 Pro', 'iPhone 17 Pro Max', 'Pixel 10', 'Pixel 10 Pro', 'Pixel 10 Pro XL', 'Pixel 9a', 'Galaxy Z Fold 7', 'Galaxy Z Fold 7 Cover', 'Galaxy Z Flip 7', 'Galaxy Z Flip 7 Cover', 'HUAWEI Mate 80', 'HUAWEI Pura 80 Pro', 'HUAWEI nova 14 Ultra', 'HUAWEI Pura X (inner)', 'HUAWEI Pura X (outer)']), + ...byYear(2026, ['iPhone 17e', 'HUAWEI Pura X Max (outer)', 'HUAWEI Pura X Max (inner)']), +} + +export function withReleaseYears(devices: readonly DeviceProfile[]): readonly PresetDeviceProfile[] { + return devices.map((device) => { + const releaseYear = RELEASE_YEAR_BY_NAME[device.name] + if (releaseYear === undefined) throw new Error(`No release year recorded for ${JSON.stringify(device.name)}`) + return { ...device, releaseYear } + }) +} diff --git a/packages/devices/src/profile-validation.test.ts b/packages/devices/src/profile-validation.test.ts index b560a9c..148fa7b 100644 --- a/packages/devices/src/profile-validation.test.ts +++ b/packages/devices/src/profile-validation.test.ts @@ -118,6 +118,10 @@ describe('assertDeviceProfile only checks the optional numeric fields when they it.each(['name', 'system', 'userAgent'] as const)('a numeric %s is rejected', (field) => { expectRejects({ ...VALID, [field]: 42 }, undefined, `deviceProfile.${field}`) }) + + it.each([2024.5, 1999, 2101])('an invalid release year (%s) is rejected', (releaseYear) => { + expectRejects({ ...VALID, releaseYear }, undefined, 'deviceProfile.releaseYear') + }) }) describe('the label argument replaces the "deviceProfile" prefix in every message', () => { diff --git a/packages/devices/src/validate.ts b/packages/devices/src/validate.ts index 15cd11f..ea20bb8 100644 --- a/packages/devices/src/validate.ts +++ b/packages/devices/src/validate.ts @@ -86,6 +86,12 @@ export function assertDeviceProfile(value: unknown, label = 'deviceProfile'): as expectString(`${label}.name`, value.name) expectFiniteAtLeast(`${label}.pixelRatio`, value.pixelRatio, 0, true) + if (value.releaseYear !== undefined) { + const releaseYear = value.releaseYear + if (typeof releaseYear !== 'number' || !Number.isInteger(releaseYear) || releaseYear < 2000 || releaseYear > 2100) { + throw new TypeError(`${label}.releaseYear must be an integer between 2000 and 2100, got ${releaseYear}`) + } + } for (const field of NONNEGATIVE_HEIGHT_FIELDS) { if (value[field] !== undefined) { From d517895f9d720b2680d76c51c7f81c55843f06ce Mon Sep 17 00:00:00 2001 From: lbb00 Date: Thu, 10 Sep 2026 15:11:41 +0800 Subject: [PATCH 2/3] refactor(devices): inline preset release years --- packages/devices/src/presets/android.ts | 177 +++++++++---------- packages/devices/src/presets/harmony.ts | 49 +++-- packages/devices/src/presets/ios.ts | 131 +++++++------- packages/devices/src/presets/release-year.ts | 37 ---- 4 files changed, 177 insertions(+), 217 deletions(-) delete mode 100644 packages/devices/src/presets/release-year.ts diff --git a/packages/devices/src/presets/android.ts b/packages/devices/src/presets/android.ts index db56f81..ca9bc32 100644 --- a/packages/devices/src/presets/android.ts +++ b/packages/devices/src/presets/android.ts @@ -5,96 +5,95 @@ * devices.ts — see the README for what each source could and could not tell us. */ import type { PresetDeviceProfile } from '../devices.js' -import { withReleaseYears } from './release-year.js' /** Android phones and tablets. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const ANDROID_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ - { name: 'Pixel 7', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', statusBarHeight: 52, safeAreaInsets: { top: 52 }, safeAreaInsetsLandscape: { right: 52 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 8', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 8 Pro', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 8a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 46, safeAreaInsets: { top: 46 }, safeAreaInsetsLandscape: { right: 46 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 9', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 9 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 9 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 66, safeAreaInsets: { top: 66 }, safeAreaInsetsLandscape: { right: 66 }, cutout: { shape: 'circle', width: 32, height: 32, top: 17 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 10', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 16', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Samsung Galaxy S8+', os: 'android', screen: { width: 360, height: 740 }, pixelRatio: 4, system: 'Android 7.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; SM-G955U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Samsung Galaxy S20 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 3.5, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36' }, - { name: 'Surface Duo', os: 'android', screen: { width: 540, height: 720 }, pixelRatio: 2.5, system: 'Android 11.0' }, +export const ANDROID_DEVICES: readonly PresetDeviceProfile[] = [ + { name: 'Pixel 7', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', statusBarHeight: 52, safeAreaInsets: { top: 52 }, safeAreaInsetsLandscape: { right: 52 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Pixel 8', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Pixel 8 Pro', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Pixel 8a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 46, safeAreaInsets: { top: 46 }, safeAreaInsetsLandscape: { right: 46 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 9', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 9 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 9 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 66, safeAreaInsets: { top: 66 }, safeAreaInsetsLandscape: { right: 66 }, cutout: { shape: 'circle', width: 32, height: 32, top: 17 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 10', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 16', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Samsung Galaxy S8+', os: 'android', screen: { width: 360, height: 740 }, pixelRatio: 4, system: 'Android 7.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; SM-G955U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, + { name: 'Samsung Galaxy S20 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 3.5, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36', releaseYear: 2020 }, + { name: 'Surface Duo', os: 'android', screen: { width: 540, height: 720 }, pixelRatio: 2.5, system: 'Android 11.0', releaseYear: 2020 }, // Table stores portrait; the unfolded (wider-than-tall) screen is what orientation=landscape reads out. - { name: 'Surface Duo (inner)', os: 'android', screen: { width: 720, height: 1114 }, pixelRatio: 2.5, system: 'Android 11.0' }, - { name: 'Samsung Galaxy A55', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 2.25, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 9 Pro Fold', os: 'android', screen: { width: 412, height: 922 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36' }, - { name: 'Pixel 9 Pro Fold (inner)', os: 'android', screen: { width: 836, height: 842 }, pixelRatio: 2.625, system: 'Android 14' }, - { name: 'Galaxy Z Fold 6', os: 'android', screen: { width: 412, height: 968 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, + { name: 'Surface Duo (inner)', os: 'android', screen: { width: 720, height: 1114 }, pixelRatio: 2.5, system: 'Android 11.0', releaseYear: 2020 }, + { name: 'Samsung Galaxy A55', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 2.25, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 9 Pro Fold', os: 'android', screen: { width: 412, height: 922 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Pixel 9 Pro Fold (inner)', os: 'android', screen: { width: 836, height: 842 }, pixelRatio: 2.625, system: 'Android 14', releaseYear: 2024 }, + { name: 'Galaxy Z Fold 6', os: 'android', screen: { width: 412, height: 968 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, // 707 x 823 @ 2.625 reproduces the panel's 1856 x 2160 physical pixels (Samsung's spec sheet) within 1 px, the same density the Fold 5 inner row uses. - { name: 'Galaxy Z Fold 6 (inner)', os: 'android', screen: { width: 707, height: 823 }, pixelRatio: 2.625, system: 'Android 14' }, - { name: 'Galaxy Z Fold 5', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F946B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'Galaxy Z Fold 5 (inner)', os: 'android', screen: { width: 690, height: 829 }, pixelRatio: 2.625, system: 'Android 13' }, - { name: 'Samsung Galaxy A51/71', os: 'android', screen: { width: 412, height: 914 }, pixelRatio: 2.625, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-A515F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36' }, - { name: 'Galaxy Tab S4', os: 'android', formFactor: 'tablet', screen: { width: 712, height: 1138 }, pixelRatio: 2.25, system: 'Android 8.1', userAgent: 'Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36' }, - { name: 'Moto G4', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Moto G Power', os: 'android', screen: { width: 412, height: 823 }, pixelRatio: 1.75, system: 'Android 11' }, - { name: 'Nexus 5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36' }, - { name: 'Nexus 5X', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36' }, - { name: 'Nexus 6', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 7.1', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36' }, - { name: 'Galaxy Note 3', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 4.3', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30' }, - { name: 'Galaxy Note II', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.1', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30' }, - { name: 'Galaxy S III', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.0', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30' }, - { name: 'Galaxy S5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 5.0', userAgent: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy S9+', os: 'android', screen: { width: 320, height: 658 }, pixelRatio: 4.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy S24', os: 'android', screen: { width: 360, height: 780 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Tab S9', os: 'android', formFactor: 'tablet', screen: { width: 640, height: 1024 }, pixelRatio: 2.5, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36' }, - { name: 'Galaxy Z Fold 6 Cover', os: 'android', screen: { width: 484, height: 1188 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Fold 7', os: 'android', screen: { width: 984, height: 1092 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Fold 7 Cover', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Flip 6', os: 'android', screen: { width: 360, height: 880 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Flip 6 Cover', os: 'android', screen: { width: 360, height: 374 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Flip 7', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Galaxy Z Flip 7 Cover', os: 'android', screen: { width: 474, height: 524 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'LG Optimus L70', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 1.25, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Nexus 10', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 5.1', userAgent: 'Mozilla/5.0 (Linux; Android 5.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36' }, - { name: 'Nexus 4', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 2, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Nexus 6P', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Nexus 7', os: 'android', formFactor: 'tablet', screen: { width: 600, height: 960 }, pixelRatio: 2, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36' }, - { name: 'Pixel 2', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 2 XL', os: 'android', screen: { width: 411, height: 823 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 3', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 9', userAgent: 'Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 4', os: 'android', screen: { width: 353, height: 745 }, pixelRatio: 3, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 4a (5G)', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 2.63, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 5', os: 'android', screen: { width: 393, height: 851 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 6', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 6 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 6a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 7 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 13', statusBarHeight: 41, cutout: { shape: 'circle', width: 25, height: 25, top: 8 }, userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 7a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 10 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 16', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 10 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36' }, - { name: 'Pixel 3 XL', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36' }, - { name: 'Pixel Fold', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36' }, - { name: 'Pixel Tablet', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Tablet) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' }, - { name: 'Pixel 9a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 15', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy Fold', os: 'android', screen: { width: 280, height: 653 }, pixelRatio: 3, system: 'Android 9.0' }, - { name: 'Galaxy S21', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 11' }, - { name: 'Galaxy S21 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 2.8125, system: 'Android 11' }, - { name: 'Galaxy S21 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 11' }, - { name: 'Galaxy S20', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 10' }, - { name: 'Galaxy S20 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 10' }, - { name: 'Samsung S21 FE', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SAMSUNG SM-G990B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/20.0 Chrome/106.0.5249.126 Mobile Safari/537.36' }, - { name: 'Galaxy Fold3 (Folded)', os: 'android', screen: { width: 320, height: 872 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'Galaxy Fold3 (Unfolded)', os: 'android', screen: { width: 590, height: 736 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'Galaxy S22', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S22 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S906B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S22 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S23', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S23 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S23 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S24 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S926B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy S24 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Galaxy Z Flip 5', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F731B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'Galaxy Z Fold 4', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F936B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'OnePlus 12', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; CPH2581) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Nothing Phone 2', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; A065) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36' }, - { name: 'Xiaomi 14', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; 23127PN0CC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36' }, - { name: 'Motorola Razr+', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; XT2321-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, - { name: 'OnePlus Open', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; CPH2551) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36' }, -]) + { name: 'Galaxy Z Fold 6 (inner)', os: 'android', screen: { width: 707, height: 823 }, pixelRatio: 2.625, system: 'Android 14', releaseYear: 2024 }, + { name: 'Galaxy Z Fold 5', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F946B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy Z Fold 5 (inner)', os: 'android', screen: { width: 690, height: 829 }, pixelRatio: 2.625, system: 'Android 13', releaseYear: 2023 }, + { name: 'Samsung Galaxy A51/71', os: 'android', screen: { width: 412, height: 914 }, pixelRatio: 2.625, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-A515F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36', releaseYear: 2019 }, + { name: 'Galaxy Tab S4', os: 'android', formFactor: 'tablet', screen: { width: 712, height: 1138 }, pixelRatio: 2.25, system: 'Android 8.1', userAgent: 'Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2018 }, + { name: 'Moto G4', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2016 }, + { name: 'Moto G Power', os: 'android', screen: { width: 412, height: 823 }, pixelRatio: 1.75, system: 'Android 11', releaseYear: 2021 }, + { name: 'Nexus 5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2013 }, + { name: 'Nexus 5X', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2015 }, + { name: 'Nexus 6', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 7.1', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2014 }, + { name: 'Galaxy Note 3', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 4.3', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2013 }, + { name: 'Galaxy Note II', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.1', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2012 }, + { name: 'Galaxy S III', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.0', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2012 }, + { name: 'Galaxy S5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 5.0', userAgent: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2014 }, + { name: 'Galaxy S9+', os: 'android', screen: { width: 320, height: 658 }, pixelRatio: 4.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2018 }, + { name: 'Galaxy S24', os: 'android', screen: { width: 360, height: 780 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy Tab S9', os: 'android', formFactor: 'tablet', screen: { width: 640, height: 1024 }, pixelRatio: 2.5, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy Z Fold 6 Cover', os: 'android', screen: { width: 484, height: 1188 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy Z Fold 7', os: 'android', screen: { width: 984, height: 1092 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Galaxy Z Fold 7 Cover', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Galaxy Z Flip 6', os: 'android', screen: { width: 360, height: 880 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy Z Flip 6 Cover', os: 'android', screen: { width: 360, height: 374 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy Z Flip 7', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Galaxy Z Flip 7 Cover', os: 'android', screen: { width: 474, height: 524 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'LG Optimus L70', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 1.25, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2014 }, + { name: 'Nexus 10', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 5.1', userAgent: 'Mozilla/5.0 (Linux; Android 5.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2012 }, + { name: 'Nexus 4', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 2, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2012 }, + { name: 'Nexus 6P', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2015 }, + { name: 'Nexus 7', os: 'android', formFactor: 'tablet', screen: { width: 600, height: 960 }, pixelRatio: 2, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2012 }, + { name: 'Pixel 2', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, + { name: 'Pixel 2 XL', os: 'android', screen: { width: 411, height: 823 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, + { name: 'Pixel 3', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 9', userAgent: 'Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2018 }, + { name: 'Pixel 4', os: 'android', screen: { width: 353, height: 745 }, pixelRatio: 3, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2019 }, + { name: 'Pixel 4a (5G)', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 2.63, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2020 }, + { name: 'Pixel 5', os: 'android', screen: { width: 393, height: 851 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2020 }, + { name: 'Pixel 6', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2021 }, + { name: 'Pixel 6 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2021 }, + { name: 'Pixel 6a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Pixel 7 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 13', statusBarHeight: 41, cutout: { shape: 'circle', width: 25, height: 25, top: 8 }, userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Pixel 7a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Pixel 10 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 16', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Pixel 10 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Pixel 3 XL', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36', releaseYear: 2018 }, + { name: 'Pixel Fold', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Pixel Tablet', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Tablet) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', releaseYear: 2023 }, + { name: 'Pixel 9a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 15', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', releaseYear: 2025 }, + { name: 'Galaxy Fold', os: 'android', screen: { width: 280, height: 653 }, pixelRatio: 3, system: 'Android 9.0', releaseYear: 2019 }, + { name: 'Galaxy S21', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 11', releaseYear: 2021 }, + { name: 'Galaxy S21 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 2.8125, system: 'Android 11', releaseYear: 2021 }, + { name: 'Galaxy S21 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 11', releaseYear: 2021 }, + { name: 'Galaxy S20', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 10', releaseYear: 2020 }, + { name: 'Galaxy S20 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 10', releaseYear: 2020 }, + { name: 'Samsung S21 FE', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SAMSUNG SM-G990B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/20.0 Chrome/106.0.5249.126 Mobile Safari/537.36', releaseYear: 2021 }, + { name: 'Galaxy Fold3 (Folded)', os: 'android', screen: { width: 320, height: 872 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2021 }, + { name: 'Galaxy Fold3 (Unfolded)', os: 'android', screen: { width: 590, height: 736 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2021 }, + { name: 'Galaxy S22', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Galaxy S22 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S906B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Galaxy S22 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'Galaxy S23', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy S23 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy S23 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy S24 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S926B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy S24 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Galaxy Z Flip 5', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F731B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Galaxy Z Fold 4', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F936B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2022 }, + { name: 'OnePlus 12', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; CPH2581) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Nothing Phone 2', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; A065) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'Xiaomi 14', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; 23127PN0CC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, + { name: 'Motorola Razr+', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; XT2321-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, + { name: 'OnePlus Open', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; CPH2551) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, +] diff --git a/packages/devices/src/presets/harmony.ts b/packages/devices/src/presets/harmony.ts index acbcda0..2760fdd 100644 --- a/packages/devices/src/presets/harmony.ts +++ b/packages/devices/src/presets/harmony.ts @@ -5,31 +5,30 @@ * devices.ts — see the README for what each source could and could not tell us. */ import type { PresetDeviceProfile } from '../devices.js' -import { withReleaseYears } from './release-year.js' /** HarmonyOS phones, including both screens of each folding model. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const HARMONY_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ - { name: 'HUAWEI Mate 80', os: 'harmony', screen: { width: 366, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, - { name: 'HUAWEI Mate 70', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 } }, - { name: 'HUAWEI Mate 70 Pro', os: 'harmony', screen: { width: 376, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, - { name: 'HUAWEI Mate 60', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 33, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 33 } }, - { name: 'HUAWEI Mate 60 Pro', os: 'harmony', screen: { width: 388, height: 837 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 } }, - { name: 'HUAWEI Pura 80 Pro', os: 'harmony', screen: { width: 365, height: 814 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 } }, - { name: 'HUAWEI nova 14 Ultra', os: 'harmony', screen: { width: 363, height: 817 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 48, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 48 } }, - { name: 'HUAWEI Pura X (inner)', os: 'harmony', screen: { width: 440, height: 707 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, - { name: 'HUAWEI Pura X (outer)', os: 'harmony', screen: { width: 327, height: 327 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 0, navigationBarHeight: 0, navigationBarHeightLandscape: 0 }, - { name: 'HUAWEI Mate X6 (inner)', os: 'harmony', screen: { width: 717, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, - { name: 'HUAWEI Mate X6 (outer)', os: 'harmony', screen: { width: 346, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 } }, - { name: 'HUAWEI Pura 70', os: 'harmony', screen: { width: 372, height: 818 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 } }, - { name: 'HUAWEI Pura 70 Pro', os: 'harmony', screen: { width: 373, height: 843 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 } }, - { name: 'HUAWEI Mate X5 (inner)', os: 'harmony', screen: { width: 712, height: 799 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 } }, - { name: 'HUAWEI Mate X5 (outer)', os: 'harmony', screen: { width: 346, height: 801 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 } }, - { name: 'HUAWEI Pocket 2', os: 'harmony', screen: { width: 364, height: 861 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 } }, - { name: 'HUAWEI nova 13', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 } }, - { name: 'HUAWEI nova 13 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 } }, - { name: 'HUAWEI nova 12', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 } }, - { name: 'HUAWEI nova 12 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 } }, - { name: 'HUAWEI Pura X Max (outer)', os: 'harmony', screen: { width: 460, height: 672 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 66, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 66 } }, +export const HARMONY_DEVICES: readonly PresetDeviceProfile[] = [ + { name: 'HUAWEI Mate 80', os: 'harmony', screen: { width: 366, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2025 }, + { name: 'HUAWEI Mate 70', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 }, releaseYear: 2024 }, + { name: 'HUAWEI Mate 70 Pro', os: 'harmony', screen: { width: 376, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, + { name: 'HUAWEI Mate 60', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 33, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 33 }, releaseYear: 2023 }, + { name: 'HUAWEI Mate 60 Pro', os: 'harmony', screen: { width: 388, height: 837 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2023 }, + { name: 'HUAWEI Pura 80 Pro', os: 'harmony', screen: { width: 365, height: 814 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2025 }, + { name: 'HUAWEI nova 14 Ultra', os: 'harmony', screen: { width: 363, height: 817 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 48, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 48 }, releaseYear: 2025 }, + { name: 'HUAWEI Pura X (inner)', os: 'harmony', screen: { width: 440, height: 707 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2025 }, + { name: 'HUAWEI Pura X (outer)', os: 'harmony', screen: { width: 327, height: 327 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 0, navigationBarHeight: 0, navigationBarHeightLandscape: 0, releaseYear: 2025 }, + { name: 'HUAWEI Mate X6 (inner)', os: 'harmony', screen: { width: 717, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, + { name: 'HUAWEI Mate X6 (outer)', os: 'harmony', screen: { width: 346, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, + { name: 'HUAWEI Pura 70', os: 'harmony', screen: { width: 372, height: 818 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2024 }, + { name: 'HUAWEI Pura 70 Pro', os: 'harmony', screen: { width: 373, height: 843 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2024 }, + { name: 'HUAWEI Mate X5 (inner)', os: 'harmony', screen: { width: 712, height: 799 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2023 }, + { name: 'HUAWEI Mate X5 (outer)', os: 'harmony', screen: { width: 346, height: 801 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2023 }, + { name: 'HUAWEI Pocket 2', os: 'harmony', screen: { width: 364, height: 861 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 }, releaseYear: 2024 }, + { name: 'HUAWEI nova 13', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 }, releaseYear: 2024 }, + { name: 'HUAWEI nova 13 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 }, releaseYear: 2024 }, + { name: 'HUAWEI nova 12', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 }, releaseYear: 2023 }, + { name: 'HUAWEI nova 12 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 }, releaseYear: 2023 }, + { name: 'HUAWEI Pura X Max (outer)', os: 'harmony', screen: { width: 460, height: 672 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 66, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 66 }, releaseYear: 2026 }, // Table stores portrait; the unfolded (wider-than-tall) screen is what orientation=landscape reads out. - { name: 'HUAWEI Pura X Max (inner)', os: 'harmony', screen: { width: 665, height: 940 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 72, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 72 } }, -]) + { name: 'HUAWEI Pura X Max (inner)', os: 'harmony', screen: { width: 665, height: 940 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 72, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 72 }, releaseYear: 2026 }, +] diff --git a/packages/devices/src/presets/ios.ts b/packages/devices/src/presets/ios.ts index 23ee5cc..106f9a7 100644 --- a/packages/devices/src/presets/ios.ts +++ b/packages/devices/src/presets/ios.ts @@ -5,73 +5,72 @@ * devices.ts — see the README for what each source could and could not tell us. */ import type { PresetDeviceProfile } from '../devices.js' -import { withReleaseYears } from './release-year.js' const LEGACY_IPHONE_SHELL = { screenRadius: 0, bodyRadius: 38, bezelInsets: { top: 44, bottom: 48 }, homeButton: { diameter: 30 } } /** iPhones and iPads. Part of DEVICES; listed here so a host can offer one platform alone. */ -export const IOS_DEVICES: readonly PresetDeviceProfile[] = withReleaseYears([ - { name: 'iPhone SE', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 15.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone XR', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 } }, - { name: 'iPhone 12 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 } }, - { name: 'iPhone 14', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, shell: { screenRadius: 47 } }, - { name: 'iPhone 14 Plus', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 } }, - { name: 'iPhone 14 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, shell: { screenRadius: 55 } }, - { name: 'iPhone 14 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 15', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 15 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 15 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 15 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 16e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 } }, - { name: 'iPhone 16', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 16 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 } }, - { name: 'iPhone 16 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 } }, - { name: 'iPhone 16 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 } }, - { name: 'iPad Mini', os: 'ios', formFactor: 'tablet', screen: { width: 744, height: 1133 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPad Pro 13', os: 'ios', formFactor: 'tablet', screen: { width: 1032, height: 1376 }, pixelRatio: 2, system: 'iPadOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPhone 5', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 6/7/8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 6/7/8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone X', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 16.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, shell: { screenRadius: 39 } }, - { name: 'iPhone XS Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 } }, - { name: 'iPhone 12/13 mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 } }, - { name: 'iPhone 12/13 (Pro)', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 } }, - { name: 'iPhone 12/13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 } }, - { name: 'iPad', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 } }, - { name: 'iPad Pro 10.5-inch', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1112 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 } }, - { name: 'iPad Pro 12.9-inch', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPad (gen 5)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 } }, - { name: 'iPad (gen 6)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 } }, - { name: 'iPad (gen 7)', os: 'ios', formFactor: 'tablet', screen: { width: 810, height: 1080 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 } }, - { name: 'iPad (gen 11)', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPad Pro 11', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1194 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPhone 6', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 6 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 7', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 7 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone SE (3rd gen)', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 11', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 } }, - { name: 'iPhone 11 Pro', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 12.2', navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 } }, - { name: 'iPhone 11 Pro Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 12.2', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 } }, - { name: 'iPhone 12', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 } }, - { name: 'iPhone 12 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 } }, - { name: 'iPhone 12 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 } }, - { name: 'iPhone 13', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 } }, - { name: 'iPhone 13 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 } }, - { name: 'iPhone 13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 } }, - { name: 'iPhone 13 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 } }, - { name: 'iPhone 17', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 } }, - { name: 'iPhone Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 } }, - { name: 'iPhone 17 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 26.0', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { top: 0, left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 } }, - { name: 'iPhone 17 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 } }, - { name: 'iPhone 17e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 20 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 } }, - { name: 'iPhone 4', os: 'ios', screen: { width: 320, height: 480 }, pixelRatio: 2, system: 'iOS 7.1', statusBarHeight: 20, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPhone 5/SE', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL }, - { name: 'iPad Air', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 13.3', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPad Pro', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPhone 17 Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.0', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 } }, - { name: 'iPad Pro M4', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1210 }, pixelRatio: 2, system: 'iOS 17.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, - { name: 'iPad Air M2', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 17.4', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 } }, -]) +export const IOS_DEVICES: readonly PresetDeviceProfile[] = [ + { name: 'iPhone SE', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 15.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2020 }, + { name: 'iPhone XR', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, releaseYear: 2018 }, + { name: 'iPhone 12 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 14', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, shell: { screenRadius: 47 }, releaseYear: 2022 }, + { name: 'iPhone 14 Plus', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2022 }, + { name: 'iPhone 14 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, shell: { screenRadius: 55 }, releaseYear: 2022 }, + { name: 'iPhone 14 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2022 }, + { name: 'iPhone 15', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, + { name: 'iPhone 15 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, + { name: 'iPhone 15 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, + { name: 'iPhone 15 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, + { name: 'iPhone 16e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2025 }, + { name: 'iPhone 16', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2024 }, + { name: 'iPhone 16 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2024 }, + { name: 'iPhone 16 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2024 }, + { name: 'iPhone 16 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2024 }, + { name: 'iPad Mini', os: 'ios', formFactor: 'tablet', screen: { width: 744, height: 1133 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2021 }, + { name: 'iPad Pro 13', os: 'ios', formFactor: 'tablet', screen: { width: 1032, height: 1376 }, pixelRatio: 2, system: 'iPadOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, + { name: 'iPhone 5', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2012 }, + { name: 'iPhone 6/7/8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, + { name: 'iPhone 6/7/8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, + { name: 'iPhone X', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 16.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, shell: { screenRadius: 39 }, releaseYear: 2017 }, + { name: 'iPhone XS Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2018 }, + { name: 'iPhone 12/13 mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 12/13 (Pro)', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 12/13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2020 }, + { name: 'iPad', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, + { name: 'iPad Pro 10.5-inch', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1112 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, + { name: 'iPad Pro 12.9-inch', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2015 }, + { name: 'iPad (gen 5)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, + { name: 'iPad (gen 6)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2018 }, + { name: 'iPad (gen 7)', os: 'ios', formFactor: 'tablet', screen: { width: 810, height: 1080 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2019 }, + { name: 'iPad (gen 11)', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2025 }, + { name: 'iPad Pro 11', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1194 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2018 }, + { name: 'iPhone 6', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, + { name: 'iPhone 6 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, + { name: 'iPhone 7', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2016 }, + { name: 'iPhone 7 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2016 }, + { name: 'iPhone 8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2017 }, + { name: 'iPhone 8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2017 }, + { name: 'iPhone SE (3rd gen)', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2022 }, + { name: 'iPhone 11', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, releaseYear: 2019 }, + { name: 'iPhone 11 Pro', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 12.2', navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2019 }, + { name: 'iPhone 11 Pro Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 12.2', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2019 }, + { name: 'iPhone 12', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 12 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 12 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2020 }, + { name: 'iPhone 13', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2021 }, + { name: 'iPhone 13 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2021 }, + { name: 'iPhone 13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2021 }, + { name: 'iPhone 13 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2021 }, + { name: 'iPhone 17', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, releaseYear: 2025 }, + { name: 'iPhone Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, releaseYear: 2025 }, + { name: 'iPhone 17 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 26.0', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { top: 0, left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2025 }, + { name: 'iPhone 17 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, releaseYear: 2025 }, + { name: 'iPhone 17e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 20 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2026 }, + { name: 'iPhone 4', os: 'ios', screen: { width: 320, height: 480 }, pixelRatio: 2, system: 'iOS 7.1', statusBarHeight: 20, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2010 }, + { name: 'iPhone 5/SE', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2012 }, + { name: 'iPad Air', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 13.3', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2020 }, + { name: 'iPad Pro', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2018 }, + { name: 'iPhone 17 Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.0', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, releaseYear: 2025 }, + { name: 'iPad Pro M4', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1210 }, pixelRatio: 2, system: 'iOS 17.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, + { name: 'iPad Air M2', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 17.4', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, +] diff --git a/packages/devices/src/presets/release-year.ts b/packages/devices/src/presets/release-year.ts deleted file mode 100644 index 068755f..0000000 --- a/packages/devices/src/presets/release-year.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { DeviceProfile, PresetDeviceProfile } from '../devices.js' - -const byYear = (year: number, names: readonly string[]): Record => - Object.fromEntries(names.map((name) => [name, year])) - -/** - * Calendar year in which the model represented by each preset was released. - * Names that intentionally group several generations use the earliest model - * in that name, keeping chronological sorting deterministic without claiming - * that the grouped models were released together. - */ -const RELEASE_YEAR_BY_NAME: Readonly> = { - ...byYear(2010, ['iPhone 4']), - ...byYear(2012, ['iPhone 5', 'iPhone 5/SE', 'Galaxy Note II', 'Galaxy S III', 'Nexus 4', 'Nexus 7', 'Nexus 10']), - ...byYear(2013, ['Nexus 5', 'Galaxy Note 3']), - ...byYear(2014, ['iPhone 6', 'iPhone 6 Plus', 'iPhone 6/7/8', 'iPhone 6/7/8 Plus', 'LG Optimus L70', 'Galaxy S5', 'Nexus 6']), - ...byYear(2015, ['Nexus 5X', 'Nexus 6P', 'iPad Pro 12.9-inch']), - ...byYear(2016, ['iPhone 7', 'iPhone 7 Plus', 'Moto G4']), - ...byYear(2017, ['iPhone 8', 'iPhone 8 Plus', 'iPhone X', 'iPad', 'iPad Pro 10.5-inch', 'iPad (gen 5)', 'Samsung Galaxy S8+', 'Pixel 2', 'Pixel 2 XL']), - ...byYear(2018, ['iPhone XR', 'iPhone XS Max', 'iPad (gen 6)', 'iPad Pro 11', 'iPad Pro', 'Galaxy S9+', 'Galaxy Tab S4', 'Pixel 3', 'Pixel 3 XL']), - ...byYear(2019, ['iPhone 11', 'iPhone 11 Pro', 'iPhone 11 Pro Max', 'iPad (gen 7)', 'Pixel 4', 'Samsung Galaxy A51/71', 'Galaxy Fold']), - ...byYear(2020, ['iPhone SE', 'iPhone 12', 'iPhone 12 Pro', 'iPhone 12 Pro Max', 'iPhone 12 Mini', 'iPhone 12/13 mini', 'iPhone 12/13 (Pro)', 'iPhone 12/13 Pro Max', 'iPad Air', 'Samsung Galaxy S20 Ultra', 'Surface Duo', 'Surface Duo (inner)', 'Galaxy S20', 'Galaxy S20 Plus', 'Pixel 4a (5G)', 'Pixel 5']), - ...byYear(2021, ['iPhone 13', 'iPhone 13 Pro', 'iPhone 13 Pro Max', 'iPhone 13 Mini', 'iPad Mini', 'Moto G Power', 'Galaxy S21', 'Galaxy S21 Plus', 'Galaxy S21 Ultra', 'Samsung S21 FE', 'Galaxy Fold3 (Folded)', 'Galaxy Fold3 (Unfolded)', 'Pixel 6', 'Pixel 6 Pro']), - ...byYear(2022, ['iPhone 14', 'iPhone 14 Plus', 'iPhone 14 Pro', 'iPhone 14 Pro Max', 'iPhone SE (3rd gen)', 'Galaxy S22', 'Galaxy S22 Plus', 'Galaxy S22 Ultra', 'Pixel 6a', 'Pixel 7', 'Pixel 7 Pro', 'Galaxy Z Fold 4']), - ...byYear(2023, ['iPhone 15', 'iPhone 15 Plus', 'iPhone 15 Pro', 'iPhone 15 Pro Max', 'Galaxy Tab S9', 'Galaxy S23', 'Galaxy S23 Plus', 'Galaxy S23 Ultra', 'Galaxy Z Fold 5', 'Galaxy Z Fold 5 (inner)', 'Galaxy Z Flip 5', 'Pixel 7a', 'Pixel 8', 'Pixel 8 Pro', 'Pixel Fold', 'Pixel Tablet', 'Nothing Phone 2', 'Motorola Razr+', 'OnePlus Open', 'HUAWEI Mate 60', 'HUAWEI Mate 60 Pro', 'HUAWEI Mate X5 (inner)', 'HUAWEI Mate X5 (outer)', 'HUAWEI nova 12', 'HUAWEI nova 12 Pro']), - ...byYear(2024, ['iPhone 16', 'iPhone 16 Plus', 'iPhone 16 Pro', 'iPhone 16 Pro Max', 'iPad Pro 13', 'iPad Pro M4', 'iPad Air M2', 'Samsung Galaxy A55', 'Pixel 8a', 'Pixel 9', 'Pixel 9 Pro', 'Pixel 9 Pro XL', 'Pixel 9 Pro Fold', 'Pixel 9 Pro Fold (inner)', 'Galaxy S24', 'Galaxy S24 Plus', 'Galaxy S24 Ultra', 'Galaxy Z Fold 6', 'Galaxy Z Fold 6 (inner)', 'Galaxy Z Fold 6 Cover', 'Galaxy Z Flip 6', 'Galaxy Z Flip 6 Cover', 'OnePlus 12', 'Xiaomi 14', 'HUAWEI Mate 70', 'HUAWEI Mate 70 Pro', 'HUAWEI Mate X6 (inner)', 'HUAWEI Mate X6 (outer)', 'HUAWEI Pura 70', 'HUAWEI Pura 70 Pro', 'HUAWEI Pocket 2', 'HUAWEI nova 13', 'HUAWEI nova 13 Pro']), - ...byYear(2025, ['iPhone 16e', 'iPad (gen 11)', 'iPhone 17', 'iPhone Air', 'iPhone 17 Air', 'iPhone 17 Pro', 'iPhone 17 Pro Max', 'Pixel 10', 'Pixel 10 Pro', 'Pixel 10 Pro XL', 'Pixel 9a', 'Galaxy Z Fold 7', 'Galaxy Z Fold 7 Cover', 'Galaxy Z Flip 7', 'Galaxy Z Flip 7 Cover', 'HUAWEI Mate 80', 'HUAWEI Pura 80 Pro', 'HUAWEI nova 14 Ultra', 'HUAWEI Pura X (inner)', 'HUAWEI Pura X (outer)']), - ...byYear(2026, ['iPhone 17e', 'HUAWEI Pura X Max (outer)', 'HUAWEI Pura X Max (inner)']), -} - -export function withReleaseYears(devices: readonly DeviceProfile[]): readonly PresetDeviceProfile[] { - return devices.map((device) => { - const releaseYear = RELEASE_YEAR_BY_NAME[device.name] - if (releaseYear === undefined) throw new Error(`No release year recorded for ${JSON.stringify(device.name)}`) - return { ...device, releaseYear } - }) -} From 51cd3965355f6cd45a0f379c883ed77a71a3ee78 Mon Sep 17 00:00:00 2001 From: lbb00 Date: Thu, 10 Sep 2026 15:22:47 +0800 Subject: [PATCH 3/3] chore(tooling): add oxc formatting checks --- .github/workflows/ci.yml | 2 + .oxfmtrc.jsonc | 7 + .oxlintrc.jsonc | 7 + package.json | 7 +- packages/devices/src/presets/android.ts | 968 +++++++++++++++++++++--- packages/devices/src/presets/harmony.ts | 285 ++++++- packages/devices/src/presets/ios.ts | 920 ++++++++++++++++++++-- pnpm-lock.yaml | 426 +++++++++++ 8 files changed, 2449 insertions(+), 173 deletions(-) create mode 100644 .oxfmtrc.jsonc create mode 100644 .oxlintrc.jsonc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cc6bfa..42bc736 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,8 @@ jobs: - run: pnpm install --frozen-lockfile + - run: pnpm format:check + - run: pnpm build - run: pnpm lint diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc new file mode 100644 index 0000000..e24f645 --- /dev/null +++ b/.oxfmtrc.jsonc @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 100, + "singleQuote": true, + "semi": false, + "trailingComma": "all", +} diff --git a/.oxlintrc.jsonc b/.oxlintrc.jsonc new file mode 100644 index 0000000..7eabce1 --- /dev/null +++ b/.oxlintrc.jsonc @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "ignorePatterns": [ + "**/coverage/**", + "**/dist/**", + ], +} diff --git a/package.json b/package.json index 3eabfbb..85c4b5b 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,20 @@ "scripts": { "build": "pnpm -r build", "check-types": "pnpm -r check-types", + "format": "oxfmt packages/devices/src/presets/ios.ts packages/devices/src/presets/android.ts packages/devices/src/presets/harmony.ts", + "format:check": "oxfmt --check packages/devices/src/presets/ios.ts packages/devices/src/presets/android.ts packages/devices/src/presets/harmony.ts", "test": "pnpm -r test && pnpm run test:scripts", "test:scripts": "node --test scripts/*.test.mjs", "test:coverage": "pnpm -r test:coverage", - "lint": "pnpm -r lint", + "lint": "pnpm -r lint && pnpm lint:oxc", + "lint:oxc": "oxlint .", "verify:pack": "pnpm -r build && node scripts/verify-pack.mjs", "images": "node scripts/render-readme-images.mjs" }, "devDependencies": { "eslint": "^10.2.1", + "oxfmt": "0.67.0", + "oxlint": "1.82.0", "typescript": "5.9.2", "typescript-eslint": "^8.58.2" }, diff --git a/packages/devices/src/presets/android.ts b/packages/devices/src/presets/android.ts index ca9bc32..8a3a059 100644 --- a/packages/devices/src/presets/android.ts +++ b/packages/devices/src/presets/android.ts @@ -8,92 +8,888 @@ import type { PresetDeviceProfile } from '../devices.js' /** Android phones and tablets. Part of DEVICES; listed here so a host can offer one platform alone. */ export const ANDROID_DEVICES: readonly PresetDeviceProfile[] = [ - { name: 'Pixel 7', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', statusBarHeight: 52, safeAreaInsets: { top: 52 }, safeAreaInsetsLandscape: { right: 52 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Pixel 8', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Pixel 8 Pro', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 50, safeAreaInsets: { top: 50 }, safeAreaInsetsLandscape: { right: 50 }, cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Pixel 8a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 46, safeAreaInsets: { top: 46 }, safeAreaInsetsLandscape: { right: 46 }, cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 9', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 14', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 9 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 9 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 14', statusBarHeight: 66, safeAreaInsets: { top: 66 }, safeAreaInsetsLandscape: { right: 66 }, cutout: { shape: 'circle', width: 32, height: 32, top: 17 }, userAgent: 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 10', os: 'android', screen: { width: 412, height: 924 }, pixelRatio: 2.625, system: 'Android 16', statusBarHeight: 58, safeAreaInsets: { top: 58 }, safeAreaInsetsLandscape: { right: 58 }, cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Samsung Galaxy S8+', os: 'android', screen: { width: 360, height: 740 }, pixelRatio: 4, system: 'Android 7.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; SM-G955U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, - { name: 'Samsung Galaxy S20 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 3.5, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36', releaseYear: 2020 }, - { name: 'Surface Duo', os: 'android', screen: { width: 540, height: 720 }, pixelRatio: 2.5, system: 'Android 11.0', releaseYear: 2020 }, + { + name: 'Pixel 7', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 13', + statusBarHeight: 52, + safeAreaInsets: { top: 52 }, + safeAreaInsetsLandscape: { right: 52 }, + cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Pixel 8', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + statusBarHeight: 50, + safeAreaInsets: { top: 50 }, + safeAreaInsetsLandscape: { right: 50 }, + cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Pixel 8 Pro', + os: 'android', + screen: { width: 448, height: 997 }, + pixelRatio: 3, + system: 'Android 14', + statusBarHeight: 50, + safeAreaInsets: { top: 50 }, + safeAreaInsetsLandscape: { right: 50 }, + cutout: { shape: 'circle', width: 28, height: 28, top: 11 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Pixel 8a', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + statusBarHeight: 46, + safeAreaInsets: { top: 46 }, + safeAreaInsetsLandscape: { right: 46 }, + cutout: { shape: 'circle', width: 26, height: 26, top: 13 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 8a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 9', + os: 'android', + screen: { width: 412, height: 924 }, + pixelRatio: 2.625, + system: 'Android 14', + statusBarHeight: 58, + safeAreaInsets: { top: 58 }, + safeAreaInsetsLandscape: { right: 58 }, + cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 9 Pro', + os: 'android', + screen: { width: 427, height: 952 }, + pixelRatio: 3, + system: 'Android 14', + statusBarHeight: 68, + safeAreaInsets: { top: 68 }, + safeAreaInsetsLandscape: { right: 68 }, + cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 9 Pro XL', + os: 'android', + screen: { width: 448, height: 997 }, + pixelRatio: 3, + system: 'Android 14', + statusBarHeight: 66, + safeAreaInsets: { top: 66 }, + safeAreaInsetsLandscape: { right: 66 }, + cutout: { shape: 'circle', width: 32, height: 32, top: 17 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 10', + os: 'android', + screen: { width: 412, height: 924 }, + pixelRatio: 2.625, + system: 'Android 16', + statusBarHeight: 58, + safeAreaInsets: { top: 58 }, + safeAreaInsetsLandscape: { right: 58 }, + cutout: { shape: 'circle', width: 28, height: 28, top: 15 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 16; Pixel 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Samsung Galaxy S8+', + os: 'android', + screen: { width: 360, height: 740 }, + pixelRatio: 4, + system: 'Android 7.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 7.0; SM-G955U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2017, + }, + { + name: 'Samsung Galaxy S20 Ultra', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 3.5, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36', + releaseYear: 2020, + }, + { + name: 'Surface Duo', + os: 'android', + screen: { width: 540, height: 720 }, + pixelRatio: 2.5, + system: 'Android 11.0', + releaseYear: 2020, + }, // Table stores portrait; the unfolded (wider-than-tall) screen is what orientation=landscape reads out. - { name: 'Surface Duo (inner)', os: 'android', screen: { width: 720, height: 1114 }, pixelRatio: 2.5, system: 'Android 11.0', releaseYear: 2020 }, - { name: 'Samsung Galaxy A55', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 2.25, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 9 Pro Fold', os: 'android', screen: { width: 412, height: 922 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Pixel 9 Pro Fold (inner)', os: 'android', screen: { width: 836, height: 842 }, pixelRatio: 2.625, system: 'Android 14', releaseYear: 2024 }, - { name: 'Galaxy Z Fold 6', os: 'android', screen: { width: 412, height: 968 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, + { + name: 'Surface Duo (inner)', + os: 'android', + screen: { width: 720, height: 1114 }, + pixelRatio: 2.5, + system: 'Android 11.0', + releaseYear: 2020, + }, + { + name: 'Samsung Galaxy A55', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 2.25, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-A556B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 9 Pro Fold', + os: 'android', + screen: { width: 412, height: 922 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 15; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Pixel 9 Pro Fold (inner)', + os: 'android', + screen: { width: 836, height: 842 }, + pixelRatio: 2.625, + system: 'Android 14', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Fold 6', + os: 'android', + screen: { width: 412, height: 968 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, // 707 x 823 @ 2.625 reproduces the panel's 1856 x 2160 physical pixels (Samsung's spec sheet) within 1 px, the same density the Fold 5 inner row uses. - { name: 'Galaxy Z Fold 6 (inner)', os: 'android', screen: { width: 707, height: 823 }, pixelRatio: 2.625, system: 'Android 14', releaseYear: 2024 }, - { name: 'Galaxy Z Fold 5', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F946B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy Z Fold 5 (inner)', os: 'android', screen: { width: 690, height: 829 }, pixelRatio: 2.625, system: 'Android 13', releaseYear: 2023 }, - { name: 'Samsung Galaxy A51/71', os: 'android', screen: { width: 412, height: 914 }, pixelRatio: 2.625, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-A515F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36', releaseYear: 2019 }, - { name: 'Galaxy Tab S4', os: 'android', formFactor: 'tablet', screen: { width: 712, height: 1138 }, pixelRatio: 2.25, system: 'Android 8.1', userAgent: 'Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2018 }, - { name: 'Moto G4', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2016 }, - { name: 'Moto G Power', os: 'android', screen: { width: 412, height: 823 }, pixelRatio: 1.75, system: 'Android 11', releaseYear: 2021 }, - { name: 'Nexus 5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 6.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2013 }, - { name: 'Nexus 5X', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2015 }, - { name: 'Nexus 6', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 7.1', shell: { screenRadius: 2 }, userAgent: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', releaseYear: 2014 }, - { name: 'Galaxy Note 3', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 4.3', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2013 }, - { name: 'Galaxy Note II', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.1', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2012 }, - { name: 'Galaxy S III', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 2, system: 'Android 4.0', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', releaseYear: 2012 }, - { name: 'Galaxy S5', os: 'android', screen: { width: 360, height: 640 }, pixelRatio: 3, system: 'Android 5.0', userAgent: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2014 }, - { name: 'Galaxy S9+', os: 'android', screen: { width: 320, height: 658 }, pixelRatio: 4.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2018 }, - { name: 'Galaxy S24', os: 'android', screen: { width: 360, height: 780 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy Tab S9', os: 'android', formFactor: 'tablet', screen: { width: 640, height: 1024 }, pixelRatio: 2.5, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy Z Fold 6 Cover', os: 'android', screen: { width: 484, height: 1188 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy Z Fold 7', os: 'android', screen: { width: 984, height: 1092 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Galaxy Z Fold 7 Cover', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Galaxy Z Flip 6', os: 'android', screen: { width: 360, height: 880 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy Z Flip 6 Cover', os: 'android', screen: { width: 360, height: 374 }, pixelRatio: 2, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy Z Flip 7', os: 'android', screen: { width: 360, height: 840 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Galaxy Z Flip 7 Cover', os: 'android', screen: { width: 474, height: 524 }, pixelRatio: 2, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'LG Optimus L70', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 1.25, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2014 }, - { name: 'Nexus 10', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 5.1', userAgent: 'Mozilla/5.0 (Linux; Android 5.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2012 }, - { name: 'Nexus 4', os: 'android', screen: { width: 384, height: 640 }, pixelRatio: 2, system: 'Android 4.4', userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2012 }, - { name: 'Nexus 6P', os: 'android', screen: { width: 412, height: 732 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2015 }, - { name: 'Nexus 7', os: 'android', formFactor: 'tablet', screen: { width: 600, height: 960 }, pixelRatio: 2, system: 'Android 6.0', userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', releaseYear: 2012 }, - { name: 'Pixel 2', os: 'android', screen: { width: 411, height: 731 }, pixelRatio: 2.625, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, - { name: 'Pixel 2 XL', os: 'android', screen: { width: 411, height: 823 }, pixelRatio: 3.5, system: 'Android 8.0', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2017 }, - { name: 'Pixel 3', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 9', userAgent: 'Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2018 }, - { name: 'Pixel 4', os: 'android', screen: { width: 353, height: 745 }, pixelRatio: 3, system: 'Android 10', userAgent: 'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2019 }, - { name: 'Pixel 4a (5G)', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 2.63, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2020 }, - { name: 'Pixel 5', os: 'android', screen: { width: 393, height: 851 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2020 }, - { name: 'Pixel 6', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2021 }, - { name: 'Pixel 6 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2021 }, - { name: 'Pixel 6a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 12', userAgent: 'Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Pixel 7 Pro', os: 'android', screen: { width: 412, height: 892 }, pixelRatio: 3.5, system: 'Android 13', statusBarHeight: 41, cutout: { shape: 'circle', width: 25, height: 25, top: 8 }, userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Pixel 7a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Pixel 10 Pro', os: 'android', screen: { width: 427, height: 952 }, pixelRatio: 3, system: 'Android 16', statusBarHeight: 68, safeAreaInsets: { top: 68 }, safeAreaInsetsLandscape: { right: 68 }, cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Pixel 10 Pro XL', os: 'android', screen: { width: 448, height: 997 }, pixelRatio: 3, system: 'Android 16', userAgent: 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Pixel 3 XL', os: 'android', screen: { width: 393, height: 786 }, pixelRatio: 2.75, system: 'Android 11', userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36', releaseYear: 2018 }, - { name: 'Pixel Fold', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Pixel Tablet', os: 'android', formFactor: 'tablet', screen: { width: 800, height: 1280 }, pixelRatio: 2, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel Tablet) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', releaseYear: 2023 }, - { name: 'Pixel 9a', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 15', userAgent: 'Mozilla/5.0 (Linux; Android 15; Pixel 9a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', releaseYear: 2025 }, - { name: 'Galaxy Fold', os: 'android', screen: { width: 280, height: 653 }, pixelRatio: 3, system: 'Android 9.0', releaseYear: 2019 }, - { name: 'Galaxy S21', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 11', releaseYear: 2021 }, - { name: 'Galaxy S21 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 2.8125, system: 'Android 11', releaseYear: 2021 }, - { name: 'Galaxy S21 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 11', releaseYear: 2021 }, - { name: 'Galaxy S20', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 10', releaseYear: 2020 }, - { name: 'Galaxy S20 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 10', releaseYear: 2020 }, - { name: 'Samsung S21 FE', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SAMSUNG SM-G990B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/20.0 Chrome/106.0.5249.126 Mobile Safari/537.36', releaseYear: 2021 }, - { name: 'Galaxy Fold3 (Folded)', os: 'android', screen: { width: 320, height: 872 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2021 }, - { name: 'Galaxy Fold3 (Unfolded)', os: 'android', screen: { width: 590, height: 736 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2021 }, - { name: 'Galaxy S22', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Galaxy S22 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S906B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Galaxy S22 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'Galaxy S23', os: 'android', screen: { width: 360, height: 800 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy S23 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy S23 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy S24 Plus', os: 'android', screen: { width: 384, height: 854 }, pixelRatio: 3, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S926B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy S24 Ultra', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Galaxy Z Flip 5', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F731B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Galaxy Z Fold 4', os: 'android', screen: { width: 344, height: 882 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-F936B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2022 }, - { name: 'OnePlus 12', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; CPH2581) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Nothing Phone 2', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; A065) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'Xiaomi 14', os: 'android', screen: { width: 412, height: 915 }, pixelRatio: 2.625, system: 'Android 14', userAgent: 'Mozilla/5.0 (Linux; Android 14; 23127PN0CC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', releaseYear: 2024 }, - { name: 'Motorola Razr+', os: 'android', screen: { width: 264, height: 844 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; XT2321-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, - { name: 'OnePlus Open', os: 'android', screen: { width: 734, height: 1014 }, pixelRatio: 2.8, system: 'Android 13', userAgent: 'Mozilla/5.0 (Linux; Android 13; CPH2551) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', releaseYear: 2023 }, + { + name: 'Galaxy Z Fold 6 (inner)', + os: 'android', + screen: { width: 707, height: 823 }, + pixelRatio: 2.625, + system: 'Android 14', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Fold 5', + os: 'android', + screen: { width: 344, height: 882 }, + pixelRatio: 2.625, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-F946B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy Z Fold 5 (inner)', + os: 'android', + screen: { width: 690, height: 829 }, + pixelRatio: 2.625, + system: 'Android 13', + releaseYear: 2023, + }, + { + name: 'Samsung Galaxy A51/71', + os: 'android', + screen: { width: 412, height: 914 }, + pixelRatio: 2.625, + system: 'Android 10', + userAgent: + 'Mozilla/5.0 (Linux; Android 10; SM-A515F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36', + releaseYear: 2019, + }, + { + name: 'Galaxy Tab S4', + os: 'android', + formFactor: 'tablet', + screen: { width: 712, height: 1138 }, + pixelRatio: 2.25, + system: 'Android 8.1', + userAgent: + 'Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', + releaseYear: 2018, + }, + { + name: 'Moto G4', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 3, + system: 'Android 6.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2016, + }, + { + name: 'Moto G Power', + os: 'android', + screen: { width: 412, height: 823 }, + pixelRatio: 1.75, + system: 'Android 11', + releaseYear: 2021, + }, + { + name: 'Nexus 5', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 3, + system: 'Android 6.0', + shell: { screenRadius: 2 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', + releaseYear: 2013, + }, + { + name: 'Nexus 5X', + os: 'android', + screen: { width: 411, height: 731 }, + pixelRatio: 2.625, + system: 'Android 8.0', + shell: { screenRadius: 2 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', + releaseYear: 2015, + }, + { + name: 'Nexus 6', + os: 'android', + screen: { width: 412, height: 732 }, + pixelRatio: 3.5, + system: 'Android 7.1', + shell: { screenRadius: 2 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Mobile Safari/537.36', + releaseYear: 2014, + }, + { + name: 'Galaxy Note 3', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 3, + system: 'Android 4.3', + userAgent: + 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + releaseYear: 2013, + }, + { + name: 'Galaxy Note II', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 2, + system: 'Android 4.1', + userAgent: + 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + releaseYear: 2012, + }, + { + name: 'Galaxy S III', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 2, + system: 'Android 4.0', + userAgent: + 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + releaseYear: 2012, + }, + { + name: 'Galaxy S5', + os: 'android', + screen: { width: 360, height: 640 }, + pixelRatio: 3, + system: 'Android 5.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2014, + }, + { + name: 'Galaxy S9+', + os: 'android', + screen: { width: 320, height: 658 }, + pixelRatio: 4.5, + system: 'Android 8.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2018, + }, + { + name: 'Galaxy S24', + os: 'android', + screen: { width: 360, height: 780 }, + pixelRatio: 3, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S921U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy Tab S9', + os: 'android', + formFactor: 'tablet', + screen: { width: 640, height: 1024 }, + pixelRatio: 2.5, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy Z Fold 6 Cover', + os: 'android', + screen: { width: 484, height: 1188 }, + pixelRatio: 2, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-F956U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Fold 7', + os: 'android', + screen: { width: 984, height: 1092 }, + pixelRatio: 2, + system: 'Android 16', + userAgent: + 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Galaxy Z Fold 7 Cover', + os: 'android', + screen: { width: 360, height: 840 }, + pixelRatio: 3, + system: 'Android 16', + userAgent: + 'Mozilla/5.0 (Linux; Android 16; SM-F966U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Galaxy Z Flip 6', + os: 'android', + screen: { width: 360, height: 880 }, + pixelRatio: 3, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Flip 6 Cover', + os: 'android', + screen: { width: 360, height: 374 }, + pixelRatio: 2, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-F741U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Flip 7', + os: 'android', + screen: { width: 360, height: 840 }, + pixelRatio: 3, + system: 'Android 16', + userAgent: + 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Galaxy Z Flip 7 Cover', + os: 'android', + screen: { width: 474, height: 524 }, + pixelRatio: 2, + system: 'Android 16', + userAgent: + 'Mozilla/5.0 (Linux; Android 16; SM-F761U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'LG Optimus L70', + os: 'android', + screen: { width: 384, height: 640 }, + pixelRatio: 1.25, + system: 'Android 4.4', + userAgent: + 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2014, + }, + { + name: 'Nexus 10', + os: 'android', + formFactor: 'tablet', + screen: { width: 800, height: 1280 }, + pixelRatio: 2, + system: 'Android 5.1', + userAgent: + 'Mozilla/5.0 (Linux; Android 5.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', + releaseYear: 2012, + }, + { + name: 'Nexus 4', + os: 'android', + screen: { width: 384, height: 640 }, + pixelRatio: 2, + system: 'Android 4.4', + userAgent: + 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2012, + }, + { + name: 'Nexus 6P', + os: 'android', + screen: { width: 412, height: 732 }, + pixelRatio: 3.5, + system: 'Android 8.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2015, + }, + { + name: 'Nexus 7', + os: 'android', + formFactor: 'tablet', + screen: { width: 600, height: 960 }, + pixelRatio: 2, + system: 'Android 6.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Safari/537.36', + releaseYear: 2012, + }, + { + name: 'Pixel 2', + os: 'android', + screen: { width: 411, height: 731 }, + pixelRatio: 2.625, + system: 'Android 8.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2017, + }, + { + name: 'Pixel 2 XL', + os: 'android', + screen: { width: 411, height: 823 }, + pixelRatio: 3.5, + system: 'Android 8.0', + userAgent: + 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2017, + }, + { + name: 'Pixel 3', + os: 'android', + screen: { width: 393, height: 786 }, + pixelRatio: 2.75, + system: 'Android 9', + userAgent: + 'Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2018, + }, + { + name: 'Pixel 4', + os: 'android', + screen: { width: 353, height: 745 }, + pixelRatio: 3, + system: 'Android 10', + userAgent: + 'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2019, + }, + { + name: 'Pixel 4a (5G)', + os: 'android', + screen: { width: 412, height: 892 }, + pixelRatio: 2.63, + system: 'Android 11', + userAgent: + 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2020, + }, + { + name: 'Pixel 5', + os: 'android', + screen: { width: 393, height: 851 }, + pixelRatio: 2.75, + system: 'Android 11', + userAgent: + 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2020, + }, + { + name: 'Pixel 6', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 12', + userAgent: + 'Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2021, + }, + { + name: 'Pixel 6 Pro', + os: 'android', + screen: { width: 412, height: 892 }, + pixelRatio: 3.5, + system: 'Android 12', + userAgent: + 'Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2021, + }, + { + name: 'Pixel 6a', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 12', + userAgent: + 'Mozilla/5.0 (Linux; Android 12; Pixel 6a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Pixel 7 Pro', + os: 'android', + screen: { width: 412, height: 892 }, + pixelRatio: 3.5, + system: 'Android 13', + statusBarHeight: 41, + cutout: { shape: 'circle', width: 25, height: 25, top: 8 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Pixel 7a', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; Pixel 7a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Pixel 10 Pro', + os: 'android', + screen: { width: 427, height: 952 }, + pixelRatio: 3, + system: 'Android 16', + statusBarHeight: 68, + safeAreaInsets: { top: 68 }, + safeAreaInsetsLandscape: { right: 68 }, + cutout: { shape: 'circle', width: 32, height: 32, top: 18 }, + userAgent: + 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Pixel 10 Pro XL', + os: 'android', + screen: { width: 448, height: 997 }, + pixelRatio: 3, + system: 'Android 16', + userAgent: + 'Mozilla/5.0 (Linux; Android 16; Pixel 10 Pro XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.7827.55 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Pixel 3 XL', + os: 'android', + screen: { width: 393, height: 786 }, + pixelRatio: 2.75, + system: 'Android 11', + userAgent: + 'Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36', + releaseYear: 2018, + }, + { + name: 'Pixel Fold', + os: 'android', + screen: { width: 734, height: 1014 }, + pixelRatio: 2.8, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; Pixel Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Pixel Tablet', + os: 'android', + formFactor: 'tablet', + screen: { width: 800, height: 1280 }, + pixelRatio: 2, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; Pixel Tablet) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Pixel 9a', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 15', + userAgent: + 'Mozilla/5.0 (Linux; Android 15; Pixel 9a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36', + releaseYear: 2025, + }, + { + name: 'Galaxy Fold', + os: 'android', + screen: { width: 280, height: 653 }, + pixelRatio: 3, + system: 'Android 9.0', + releaseYear: 2019, + }, + { + name: 'Galaxy S21', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 3, + system: 'Android 11', + releaseYear: 2021, + }, + { + name: 'Galaxy S21 Plus', + os: 'android', + screen: { width: 384, height: 854 }, + pixelRatio: 2.8125, + system: 'Android 11', + releaseYear: 2021, + }, + { + name: 'Galaxy S21 Ultra', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 11', + releaseYear: 2021, + }, + { + name: 'Galaxy S20', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 3, + system: 'Android 10', + releaseYear: 2020, + }, + { + name: 'Galaxy S20 Plus', + os: 'android', + screen: { width: 384, height: 854 }, + pixelRatio: 3, + system: 'Android 10', + releaseYear: 2020, + }, + { + name: 'Samsung S21 FE', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 3, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SAMSUNG SM-G990B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/20.0 Chrome/106.0.5249.126 Mobile Safari/537.36', + releaseYear: 2021, + }, + { + name: 'Galaxy Fold3 (Folded)', + os: 'android', + screen: { width: 320, height: 872 }, + pixelRatio: 3, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2021, + }, + { + name: 'Galaxy Fold3 (Unfolded)', + os: 'android', + screen: { width: 590, height: 736 }, + pixelRatio: 3, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-F926U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2021, + }, + { + name: 'Galaxy S22', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 3, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Galaxy S22 Plus', + os: 'android', + screen: { width: 384, height: 854 }, + pixelRatio: 3, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-S906B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Galaxy S22 Ultra', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'Galaxy S23', + os: 'android', + screen: { width: 360, height: 800 }, + pixelRatio: 3, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy S23 Plus', + os: 'android', + screen: { width: 384, height: 854 }, + pixelRatio: 3, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy S23 Ultra', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy S24 Plus', + os: 'android', + screen: { width: 384, height: 854 }, + pixelRatio: 3, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S926B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy S24 Ultra', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Galaxy Z Flip 5', + os: 'android', + screen: { width: 264, height: 844 }, + pixelRatio: 2.8, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-F731B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Galaxy Z Fold 4', + os: 'android', + screen: { width: 344, height: 882 }, + pixelRatio: 2.8, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; SM-F936B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2022, + }, + { + name: 'OnePlus 12', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; CPH2581) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Nothing Phone 2', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; A065) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'Xiaomi 14', + os: 'android', + screen: { width: 412, height: 915 }, + pixelRatio: 2.625, + system: 'Android 14', + userAgent: + 'Mozilla/5.0 (Linux; Android 14; 23127PN0CC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', + releaseYear: 2024, + }, + { + name: 'Motorola Razr+', + os: 'android', + screen: { width: 264, height: 844 }, + pixelRatio: 2.8, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; XT2321-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2023, + }, + { + name: 'OnePlus Open', + os: 'android', + screen: { width: 734, height: 1014 }, + pixelRatio: 2.8, + system: 'Android 13', + userAgent: + 'Mozilla/5.0 (Linux; Android 13; CPH2551) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.131 Mobile Safari/537.36', + releaseYear: 2023, + }, ] diff --git a/packages/devices/src/presets/harmony.ts b/packages/devices/src/presets/harmony.ts index 2760fdd..0b2c83f 100644 --- a/packages/devices/src/presets/harmony.ts +++ b/packages/devices/src/presets/harmony.ts @@ -8,27 +8,268 @@ import type { PresetDeviceProfile } from '../devices.js' /** HarmonyOS phones, including both screens of each folding model. Part of DEVICES; listed here so a host can offer one platform alone. */ export const HARMONY_DEVICES: readonly PresetDeviceProfile[] = [ - { name: 'HUAWEI Mate 80', os: 'harmony', screen: { width: 366, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2025 }, - { name: 'HUAWEI Mate 70', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 }, releaseYear: 2024 }, - { name: 'HUAWEI Mate 70 Pro', os: 'harmony', screen: { width: 376, height: 809 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, - { name: 'HUAWEI Mate 60', os: 'harmony', screen: { width: 374, height: 827 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 33, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 33 }, releaseYear: 2023 }, - { name: 'HUAWEI Mate 60 Pro', os: 'harmony', screen: { width: 388, height: 837 }, pixelRatio: 3.25, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2023 }, - { name: 'HUAWEI Pura 80 Pro', os: 'harmony', screen: { width: 365, height: 814 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2025 }, - { name: 'HUAWEI nova 14 Ultra', os: 'harmony', screen: { width: 363, height: 817 }, pixelRatio: 3.5, system: 'HarmonyOS 5.0', statusBarHeight: 48, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 48 }, releaseYear: 2025 }, - { name: 'HUAWEI Pura X (inner)', os: 'harmony', screen: { width: 440, height: 707 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2025 }, - { name: 'HUAWEI Pura X (outer)', os: 'harmony', screen: { width: 327, height: 327 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 0, navigationBarHeight: 0, navigationBarHeightLandscape: 0, releaseYear: 2025 }, - { name: 'HUAWEI Mate X6 (inner)', os: 'harmony', screen: { width: 717, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, - { name: 'HUAWEI Mate X6 (outer)', os: 'harmony', screen: { width: 346, height: 781 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 39, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 39 }, releaseYear: 2024 }, - { name: 'HUAWEI Pura 70', os: 'harmony', screen: { width: 372, height: 818 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2024 }, - { name: 'HUAWEI Pura 70 Pro', os: 'harmony', screen: { width: 373, height: 843 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 38, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 38 }, releaseYear: 2024 }, - { name: 'HUAWEI Mate X5 (inner)', os: 'harmony', screen: { width: 712, height: 799 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2023 }, - { name: 'HUAWEI Mate X5 (outer)', os: 'harmony', screen: { width: 346, height: 801 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 36, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 36 }, releaseYear: 2023 }, - { name: 'HUAWEI Pocket 2', os: 'harmony', screen: { width: 364, height: 861 }, pixelRatio: 3.125, system: 'HarmonyOS 5.0', statusBarHeight: 34, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 34 }, releaseYear: 2024 }, - { name: 'HUAWEI nova 13', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 }, releaseYear: 2024 }, - { name: 'HUAWEI nova 13 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 }, releaseYear: 2024 }, - { name: 'HUAWEI nova 12', os: 'harmony', screen: { width: 361, height: 804 }, pixelRatio: 3, system: 'HarmonyOS 5.0', statusBarHeight: 35, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 35 }, releaseYear: 2023 }, - { name: 'HUAWEI nova 12 Pro', os: 'harmony', screen: { width: 363, height: 823 }, pixelRatio: 3.375, system: 'HarmonyOS 5.0', statusBarHeight: 44, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 44 }, releaseYear: 2023 }, - { name: 'HUAWEI Pura X Max (outer)', os: 'harmony', screen: { width: 460, height: 672 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 66, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 66 }, releaseYear: 2026 }, + { + name: 'HUAWEI Mate 80', + os: 'harmony', + screen: { width: 366, height: 809 }, + pixelRatio: 3.5, + system: 'HarmonyOS 5.0', + statusBarHeight: 39, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 39 }, + releaseYear: 2025, + }, + { + name: 'HUAWEI Mate 70', + os: 'harmony', + screen: { width: 374, height: 827 }, + pixelRatio: 3.25, + system: 'HarmonyOS 5.0', + statusBarHeight: 34, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 34 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Mate 70 Pro', + os: 'harmony', + screen: { width: 376, height: 809 }, + pixelRatio: 3.5, + system: 'HarmonyOS 5.0', + statusBarHeight: 39, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 39 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Mate 60', + os: 'harmony', + screen: { width: 374, height: 827 }, + pixelRatio: 3.25, + system: 'HarmonyOS 5.0', + statusBarHeight: 33, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 33 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI Mate 60 Pro', + os: 'harmony', + screen: { width: 388, height: 837 }, + pixelRatio: 3.25, + system: 'HarmonyOS 5.0', + statusBarHeight: 38, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 38 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI Pura 80 Pro', + os: 'harmony', + screen: { width: 365, height: 814 }, + pixelRatio: 3.5, + system: 'HarmonyOS 5.0', + statusBarHeight: 38, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 38 }, + releaseYear: 2025, + }, + { + name: 'HUAWEI nova 14 Ultra', + os: 'harmony', + screen: { width: 363, height: 817 }, + pixelRatio: 3.5, + system: 'HarmonyOS 5.0', + statusBarHeight: 48, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 48 }, + releaseYear: 2025, + }, + { + name: 'HUAWEI Pura X (inner)', + os: 'harmony', + screen: { width: 440, height: 707 }, + pixelRatio: 3, + system: 'HarmonyOS 5.0', + statusBarHeight: 39, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 39 }, + releaseYear: 2025, + }, + { + name: 'HUAWEI Pura X (outer)', + os: 'harmony', + screen: { width: 327, height: 327 }, + pixelRatio: 3, + system: 'HarmonyOS 5.0', + statusBarHeight: 0, + navigationBarHeight: 0, + navigationBarHeightLandscape: 0, + releaseYear: 2025, + }, + { + name: 'HUAWEI Mate X6 (inner)', + os: 'harmony', + screen: { width: 717, height: 781 }, + pixelRatio: 3.125, + system: 'HarmonyOS 5.0', + statusBarHeight: 39, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 39 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Mate X6 (outer)', + os: 'harmony', + screen: { width: 346, height: 781 }, + pixelRatio: 3.125, + system: 'HarmonyOS 5.0', + statusBarHeight: 39, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 39 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Pura 70', + os: 'harmony', + screen: { width: 372, height: 818 }, + pixelRatio: 3.375, + system: 'HarmonyOS 5.0', + statusBarHeight: 36, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 36 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Pura 70 Pro', + os: 'harmony', + screen: { width: 373, height: 843 }, + pixelRatio: 3.375, + system: 'HarmonyOS 5.0', + statusBarHeight: 38, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 38 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI Mate X5 (inner)', + os: 'harmony', + screen: { width: 712, height: 799 }, + pixelRatio: 3.125, + system: 'HarmonyOS 5.0', + statusBarHeight: 36, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 36 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI Mate X5 (outer)', + os: 'harmony', + screen: { width: 346, height: 801 }, + pixelRatio: 3.125, + system: 'HarmonyOS 5.0', + statusBarHeight: 36, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 36 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI Pocket 2', + os: 'harmony', + screen: { width: 364, height: 861 }, + pixelRatio: 3.125, + system: 'HarmonyOS 5.0', + statusBarHeight: 34, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 34 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI nova 13', + os: 'harmony', + screen: { width: 361, height: 804 }, + pixelRatio: 3, + system: 'HarmonyOS 5.0', + statusBarHeight: 35, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 35 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI nova 13 Pro', + os: 'harmony', + screen: { width: 363, height: 823 }, + pixelRatio: 3.375, + system: 'HarmonyOS 5.0', + statusBarHeight: 44, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 44 }, + releaseYear: 2024, + }, + { + name: 'HUAWEI nova 12', + os: 'harmony', + screen: { width: 361, height: 804 }, + pixelRatio: 3, + system: 'HarmonyOS 5.0', + statusBarHeight: 35, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 35 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI nova 12 Pro', + os: 'harmony', + screen: { width: 363, height: 823 }, + pixelRatio: 3.375, + system: 'HarmonyOS 5.0', + statusBarHeight: 44, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 44 }, + releaseYear: 2023, + }, + { + name: 'HUAWEI Pura X Max (outer)', + os: 'harmony', + screen: { width: 460, height: 672 }, + pixelRatio: 2.75, + system: 'HarmonyOS 5.0', + statusBarHeight: 66, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 66 }, + releaseYear: 2026, + }, // Table stores portrait; the unfolded (wider-than-tall) screen is what orientation=landscape reads out. - { name: 'HUAWEI Pura X Max (inner)', os: 'harmony', screen: { width: 665, height: 940 }, pixelRatio: 2.75, system: 'HarmonyOS 5.0', statusBarHeight: 72, navigationBarHeight: 28, navigationBarHeightLandscape: 28, safeAreaInsets: { top: 72 }, releaseYear: 2026 }, + { + name: 'HUAWEI Pura X Max (inner)', + os: 'harmony', + screen: { width: 665, height: 940 }, + pixelRatio: 2.75, + system: 'HarmonyOS 5.0', + statusBarHeight: 72, + navigationBarHeight: 28, + navigationBarHeightLandscape: 28, + safeAreaInsets: { top: 72 }, + releaseYear: 2026, + }, ] diff --git a/packages/devices/src/presets/ios.ts b/packages/devices/src/presets/ios.ts index 106f9a7..4e231ea 100644 --- a/packages/devices/src/presets/ios.ts +++ b/packages/devices/src/presets/ios.ts @@ -6,71 +6,863 @@ */ import type { PresetDeviceProfile } from '../devices.js' -const LEGACY_IPHONE_SHELL = { screenRadius: 0, bodyRadius: 38, bezelInsets: { top: 44, bottom: 48 }, homeButton: { diameter: 30 } } +const LEGACY_IPHONE_SHELL = { + screenRadius: 0, + bodyRadius: 38, + bezelInsets: { top: 44, bottom: 48 }, + homeButton: { diameter: 30 }, +} /** iPhones and iPads. Part of DEVICES; listed here so a host can offer one platform alone. */ export const IOS_DEVICES: readonly PresetDeviceProfile[] = [ - { name: 'iPhone SE', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 15.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2020 }, - { name: 'iPhone XR', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, releaseYear: 2018 }, - { name: 'iPhone 12 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 14', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, shell: { screenRadius: 47 }, releaseYear: 2022 }, - { name: 'iPhone 14 Plus', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2022 }, - { name: 'iPhone 14 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, shell: { screenRadius: 55 }, releaseYear: 2022 }, - { name: 'iPhone 14 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2022 }, - { name: 'iPhone 15', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, - { name: 'iPhone 15 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, - { name: 'iPhone 15 Pro', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, - { name: 'iPhone 15 Pro Max', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2023 }, - { name: 'iPhone 16e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2025 }, - { name: 'iPhone 16', os: 'ios', screen: { width: 393, height: 852 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2024 }, - { name: 'iPhone 16 Plus', os: 'ios', screen: { width: 430, height: 932 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 59, bottom: 34 }, safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, releaseYear: 2024 }, - { name: 'iPhone 16 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2024 }, - { name: 'iPhone 16 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.5', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2024 }, - { name: 'iPad Mini', os: 'ios', formFactor: 'tablet', screen: { width: 744, height: 1133 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2021 }, - { name: 'iPad Pro 13', os: 'ios', formFactor: 'tablet', screen: { width: 1032, height: 1376 }, pixelRatio: 2, system: 'iPadOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, - { name: 'iPhone 5', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2012 }, - { name: 'iPhone 6/7/8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, - { name: 'iPhone 6/7/8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, - { name: 'iPhone X', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 16.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, shell: { screenRadius: 39 }, releaseYear: 2017 }, - { name: 'iPhone XS Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2018 }, - { name: 'iPhone 12/13 mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 12/13 (Pro)', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 12/13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2020 }, - { name: 'iPad', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, - { name: 'iPad Pro 10.5-inch', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1112 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, - { name: 'iPad Pro 12.9-inch', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2015 }, - { name: 'iPad (gen 5)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2017 }, - { name: 'iPad (gen 6)', os: 'ios', formFactor: 'tablet', screen: { width: 768, height: 1024 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2018 }, - { name: 'iPad (gen 7)', os: 'ios', formFactor: 'tablet', screen: { width: 810, height: 1080 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 20, statusBarHeightLandscape: 20, safeAreaInsets: { top: 20 }, safeAreaInsetsLandscape: { top: 20 }, shell: { screenRadius: 0 }, releaseYear: 2019 }, - { name: 'iPad (gen 11)', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2025 }, - { name: 'iPad Pro 11', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1194 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2018 }, - { name: 'iPhone 6', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, - { name: 'iPhone 6 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2014 }, - { name: 'iPhone 7', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2016 }, - { name: 'iPhone 7 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2016 }, - { name: 'iPhone 8', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2017 }, - { name: 'iPhone 8 Plus', os: 'ios', screen: { width: 414, height: 736 }, pixelRatio: 3, system: 'iOS 11.0', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 44, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2017 }, - { name: 'iPhone SE (3rd gen)', os: 'ios', screen: { width: 375, height: 667 }, pixelRatio: 2, system: 'iOS 18.5', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2022 }, - { name: 'iPhone 11', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 2, system: 'iOS 12.2', statusBarHeight: 48, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 48, bottom: 34 }, safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, releaseYear: 2019 }, - { name: 'iPhone 11 Pro', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 12.2', navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2019 }, - { name: 'iPhone 11 Pro Max', os: 'ios', screen: { width: 414, height: 896 }, pixelRatio: 3, system: 'iOS 12.2', statusBarHeight: 44, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 44, bottom: 34 }, safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, releaseYear: 2019 }, - { name: 'iPhone 12', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 12 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 12 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 14.4', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2020 }, - { name: 'iPhone 13', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2021 }, - { name: 'iPhone 13 Pro', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2021 }, - { name: 'iPhone 13 Pro Max', os: 'ios', screen: { width: 428, height: 926 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, releaseYear: 2021 }, - { name: 'iPhone 13 Mini', os: 'ios', screen: { width: 375, height: 812 }, pixelRatio: 3, system: 'iOS 15.0', statusBarHeight: 50, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 50, bottom: 34 }, safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, releaseYear: 2021 }, - { name: 'iPhone 17', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, releaseYear: 2025 }, - { name: 'iPhone Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, releaseYear: 2025 }, - { name: 'iPhone 17 Pro', os: 'ios', screen: { width: 402, height: 874 }, pixelRatio: 3, system: 'iOS 26.0', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { top: 0, left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, shell: { screenRadius: 62 }, releaseYear: 2025 }, - { name: 'iPhone 17 Pro Max', os: 'ios', screen: { width: 440, height: 956 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 54, safeAreaInsets: { top: 62, bottom: 34 }, safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, releaseYear: 2025 }, - { name: 'iPhone 17e', os: 'ios', screen: { width: 390, height: 844 }, pixelRatio: 3, system: 'iOS 18.7', statusBarHeight: 47, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 47, bottom: 34 }, safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 20 }, cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, releaseYear: 2026 }, - { name: 'iPhone 4', os: 'ios', screen: { width: 320, height: 480 }, pixelRatio: 2, system: 'iOS 7.1', statusBarHeight: 20, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2010 }, - { name: 'iPhone 5/SE', os: 'ios', screen: { width: 320, height: 568 }, pixelRatio: 2, system: 'iOS 10.3', statusBarHeight: 20, navigationBarHeight: 44, navigationBarHeightLandscape: 32, safeAreaInsets: { top: 20 }, shell: LEGACY_IPHONE_SHELL, releaseYear: 2012 }, - { name: 'iPad Air', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 13.3', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2020 }, - { name: 'iPad Pro', os: 'ios', formFactor: 'tablet', screen: { width: 1024, height: 1366 }, pixelRatio: 2, system: 'iOS 11.0', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2018 }, - { name: 'iPhone 17 Air', os: 'ios', screen: { width: 420, height: 912 }, pixelRatio: 3, system: 'iOS 18.0', statusBarHeight: 54, safeAreaInsets: { top: 68, bottom: 34 }, safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, releaseYear: 2025 }, - { name: 'iPad Pro M4', os: 'ios', formFactor: 'tablet', screen: { width: 834, height: 1210 }, pixelRatio: 2, system: 'iOS 17.5', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, - { name: 'iPad Air M2', os: 'ios', formFactor: 'tablet', screen: { width: 820, height: 1180 }, pixelRatio: 2, system: 'iOS 17.4', statusBarHeight: 24, statusBarHeightLandscape: 24, safeAreaInsets: { top: 24, bottom: 25 }, safeAreaInsetsLandscape: { top: 24, bottom: 25 }, shell: { screenRadius: 18 }, releaseYear: 2024 }, + { + name: 'iPhone SE', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 15.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2020, + }, + { + name: 'iPhone XR', + os: 'ios', + screen: { width: 414, height: 896 }, + pixelRatio: 2, + system: 'iOS 18.5', + statusBarHeight: 48, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 48, bottom: 34 }, + safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, + cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, + releaseYear: 2018, + }, + { + name: 'iPhone 12 Pro', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 14', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + shell: { screenRadius: 47 }, + releaseYear: 2022, + }, + { + name: 'iPhone 14 Plus', + os: 'ios', + screen: { width: 428, height: 926 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, + releaseYear: 2022, + }, + { + name: 'iPhone 14 Pro', + os: 'ios', + screen: { width: 393, height: 852 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + shell: { screenRadius: 55 }, + releaseYear: 2022, + }, + { + name: 'iPhone 14 Pro Max', + os: 'ios', + screen: { width: 430, height: 932 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2022, + }, + { + name: 'iPhone 15', + os: 'ios', + screen: { width: 393, height: 852 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2023, + }, + { + name: 'iPhone 15 Plus', + os: 'ios', + screen: { width: 430, height: 932 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2023, + }, + { + name: 'iPhone 15 Pro', + os: 'ios', + screen: { width: 393, height: 852 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2023, + }, + { + name: 'iPhone 15 Pro Max', + os: 'ios', + screen: { width: 430, height: 932 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2023, + }, + { + name: 'iPhone 16e', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + releaseYear: 2025, + }, + { + name: 'iPhone 16', + os: 'ios', + screen: { width: 393, height: 852 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2024, + }, + { + name: 'iPhone 16 Plus', + os: 'ios', + screen: { width: 430, height: 932 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 59, bottom: 34 }, + safeAreaInsetsLandscape: { left: 59, right: 59, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 11 }, + releaseYear: 2024, + }, + { + name: 'iPhone 16 Pro', + os: 'ios', + screen: { width: 402, height: 874 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 62, bottom: 34 }, + safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, + shell: { screenRadius: 62 }, + releaseYear: 2024, + }, + { + name: 'iPhone 16 Pro Max', + os: 'ios', + screen: { width: 440, height: 956 }, + pixelRatio: 3, + system: 'iOS 18.5', + statusBarHeight: 54, + safeAreaInsets: { top: 62, bottom: 34 }, + safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 21 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, + shell: { screenRadius: 62 }, + releaseYear: 2024, + }, + { + name: 'iPad Mini', + os: 'ios', + formFactor: 'tablet', + screen: { width: 744, height: 1133 }, + pixelRatio: 2, + system: 'iOS 18.5', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2021, + }, + { + name: 'iPad Pro 13', + os: 'ios', + formFactor: 'tablet', + screen: { width: 1032, height: 1376 }, + pixelRatio: 2, + system: 'iPadOS 18.5', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2024, + }, + { + name: 'iPhone 5', + os: 'ios', + screen: { width: 320, height: 568 }, + pixelRatio: 2, + system: 'iOS 10.3', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2012, + }, + { + name: 'iPhone 6/7/8', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2014, + }, + { + name: 'iPhone 6/7/8 Plus', + os: 'ios', + screen: { width: 414, height: 736 }, + pixelRatio: 3, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 44, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2014, + }, + { + name: 'iPhone X', + os: 'ios', + screen: { width: 375, height: 812 }, + pixelRatio: 3, + system: 'iOS 16.0', + statusBarHeight: 44, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 44, bottom: 34 }, + safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, + cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, + shell: { screenRadius: 39 }, + releaseYear: 2017, + }, + { + name: 'iPhone XS Max', + os: 'ios', + screen: { width: 414, height: 896 }, + pixelRatio: 3, + system: 'iOS 11.0', + statusBarHeight: 44, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 44, bottom: 34 }, + safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, + cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, + releaseYear: 2018, + }, + { + name: 'iPhone 12/13 mini', + os: 'ios', + screen: { width: 375, height: 812 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 50, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 50, bottom: 34 }, + safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, + cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 12/13 (Pro)', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 12/13 Pro Max', + os: 'ios', + screen: { width: 428, height: 926 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPad', + os: 'ios', + formFactor: 'tablet', + screen: { width: 768, height: 1024 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + statusBarHeightLandscape: 20, + safeAreaInsets: { top: 20 }, + safeAreaInsetsLandscape: { top: 20 }, + shell: { screenRadius: 0 }, + releaseYear: 2017, + }, + { + name: 'iPad Pro 10.5-inch', + os: 'ios', + formFactor: 'tablet', + screen: { width: 834, height: 1112 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + statusBarHeightLandscape: 20, + safeAreaInsets: { top: 20 }, + safeAreaInsetsLandscape: { top: 20 }, + shell: { screenRadius: 0 }, + releaseYear: 2017, + }, + { + name: 'iPad Pro 12.9-inch', + os: 'ios', + formFactor: 'tablet', + screen: { width: 1024, height: 1366 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2015, + }, + { + name: 'iPad (gen 5)', + os: 'ios', + formFactor: 'tablet', + screen: { width: 768, height: 1024 }, + pixelRatio: 2, + system: 'iOS 12.2', + statusBarHeight: 20, + statusBarHeightLandscape: 20, + safeAreaInsets: { top: 20 }, + safeAreaInsetsLandscape: { top: 20 }, + shell: { screenRadius: 0 }, + releaseYear: 2017, + }, + { + name: 'iPad (gen 6)', + os: 'ios', + formFactor: 'tablet', + screen: { width: 768, height: 1024 }, + pixelRatio: 2, + system: 'iOS 12.2', + statusBarHeight: 20, + statusBarHeightLandscape: 20, + safeAreaInsets: { top: 20 }, + safeAreaInsetsLandscape: { top: 20 }, + shell: { screenRadius: 0 }, + releaseYear: 2018, + }, + { + name: 'iPad (gen 7)', + os: 'ios', + formFactor: 'tablet', + screen: { width: 810, height: 1080 }, + pixelRatio: 2, + system: 'iOS 12.2', + statusBarHeight: 20, + statusBarHeightLandscape: 20, + safeAreaInsets: { top: 20 }, + safeAreaInsetsLandscape: { top: 20 }, + shell: { screenRadius: 0 }, + releaseYear: 2019, + }, + { + name: 'iPad (gen 11)', + os: 'ios', + formFactor: 'tablet', + screen: { width: 820, height: 1180 }, + pixelRatio: 2, + system: 'iOS 18.5', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2025, + }, + { + name: 'iPad Pro 11', + os: 'ios', + formFactor: 'tablet', + screen: { width: 834, height: 1194 }, + pixelRatio: 2, + system: 'iOS 12.2', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2018, + }, + { + name: 'iPhone 6', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2014, + }, + { + name: 'iPhone 6 Plus', + os: 'ios', + screen: { width: 414, height: 736 }, + pixelRatio: 3, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 44, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2014, + }, + { + name: 'iPhone 7', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2016, + }, + { + name: 'iPhone 7 Plus', + os: 'ios', + screen: { width: 414, height: 736 }, + pixelRatio: 3, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 44, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2016, + }, + { + name: 'iPhone 8', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2017, + }, + { + name: 'iPhone 8 Plus', + os: 'ios', + screen: { width: 414, height: 736 }, + pixelRatio: 3, + system: 'iOS 11.0', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 44, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2017, + }, + { + name: 'iPhone SE (3rd gen)', + os: 'ios', + screen: { width: 375, height: 667 }, + pixelRatio: 2, + system: 'iOS 18.5', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2022, + }, + { + name: 'iPhone 11', + os: 'ios', + screen: { width: 414, height: 896 }, + pixelRatio: 2, + system: 'iOS 12.2', + statusBarHeight: 48, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 48, bottom: 34 }, + safeAreaInsetsLandscape: { left: 48, right: 48, bottom: 21 }, + cutout: { shape: 'notch', width: 231, height: 33, top: 0 }, + releaseYear: 2019, + }, + { + name: 'iPhone 11 Pro', + os: 'ios', + screen: { width: 375, height: 812 }, + pixelRatio: 3, + system: 'iOS 12.2', + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 44, bottom: 34 }, + safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, + cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, + releaseYear: 2019, + }, + { + name: 'iPhone 11 Pro Max', + os: 'ios', + screen: { width: 414, height: 896 }, + pixelRatio: 3, + system: 'iOS 12.2', + statusBarHeight: 44, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 44, bottom: 34 }, + safeAreaInsetsLandscape: { left: 44, right: 44, bottom: 21 }, + cutout: { shape: 'notch', width: 209, height: 30, top: 0 }, + releaseYear: 2019, + }, + { + name: 'iPhone 12', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 14.4', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 12 Pro Max', + os: 'ios', + screen: { width: 428, height: 926 }, + pixelRatio: 3, + system: 'iOS 14.4', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 210, height: 32, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 12 Mini', + os: 'ios', + screen: { width: 375, height: 812 }, + pixelRatio: 3, + system: 'iOS 14.4', + statusBarHeight: 50, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 50, bottom: 34 }, + safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, + cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, + releaseYear: 2020, + }, + { + name: 'iPhone 13', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + releaseYear: 2021, + }, + { + name: 'iPhone 13 Pro', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + releaseYear: 2021, + }, + { + name: 'iPhone 13 Pro Max', + os: 'ios', + screen: { width: 428, height: 926 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 21 }, + cutout: { shape: 'notch', width: 161, height: 34, top: 0 }, + releaseYear: 2021, + }, + { + name: 'iPhone 13 Mini', + os: 'ios', + screen: { width: 375, height: 812 }, + pixelRatio: 3, + system: 'iOS 15.0', + statusBarHeight: 50, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 50, bottom: 34 }, + safeAreaInsetsLandscape: { left: 50, right: 50, bottom: 21 }, + cutout: { shape: 'notch', width: 192, height: 30, top: 0 }, + releaseYear: 2021, + }, + { + name: 'iPhone 17', + os: 'ios', + screen: { width: 402, height: 874 }, + pixelRatio: 3, + system: 'iOS 18.7', + statusBarHeight: 54, + safeAreaInsets: { top: 62, bottom: 34 }, + safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, + releaseYear: 2025, + }, + { + name: 'iPhone Air', + os: 'ios', + screen: { width: 420, height: 912 }, + pixelRatio: 3, + system: 'iOS 18.7', + statusBarHeight: 54, + safeAreaInsets: { top: 68, bottom: 34 }, + safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, + releaseYear: 2025, + }, + { + name: 'iPhone 17 Pro', + os: 'ios', + screen: { width: 402, height: 874 }, + pixelRatio: 3, + system: 'iOS 26.0', + statusBarHeight: 54, + safeAreaInsets: { top: 62, bottom: 34 }, + safeAreaInsetsLandscape: { top: 0, left: 62, right: 62, bottom: 20 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, + shell: { screenRadius: 62 }, + releaseYear: 2025, + }, + { + name: 'iPhone 17 Pro Max', + os: 'ios', + screen: { width: 440, height: 956 }, + pixelRatio: 3, + system: 'iOS 18.7', + statusBarHeight: 54, + safeAreaInsets: { top: 62, bottom: 34 }, + safeAreaInsetsLandscape: { left: 62, right: 62, bottom: 20 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 14 }, + releaseYear: 2025, + }, + { + name: 'iPhone 17e', + os: 'ios', + screen: { width: 390, height: 844 }, + pixelRatio: 3, + system: 'iOS 18.7', + statusBarHeight: 47, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 47, bottom: 34 }, + safeAreaInsetsLandscape: { left: 47, right: 47, bottom: 20 }, + cutout: { shape: 'notch', width: 162, height: 34, top: 0 }, + releaseYear: 2026, + }, + { + name: 'iPhone 4', + os: 'ios', + screen: { width: 320, height: 480 }, + pixelRatio: 2, + system: 'iOS 7.1', + statusBarHeight: 20, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2010, + }, + { + name: 'iPhone 5/SE', + os: 'ios', + screen: { width: 320, height: 568 }, + pixelRatio: 2, + system: 'iOS 10.3', + statusBarHeight: 20, + navigationBarHeight: 44, + navigationBarHeightLandscape: 32, + safeAreaInsets: { top: 20 }, + shell: LEGACY_IPHONE_SHELL, + releaseYear: 2012, + }, + { + name: 'iPad Air', + os: 'ios', + formFactor: 'tablet', + screen: { width: 820, height: 1180 }, + pixelRatio: 2, + system: 'iOS 13.3', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2020, + }, + { + name: 'iPad Pro', + os: 'ios', + formFactor: 'tablet', + screen: { width: 1024, height: 1366 }, + pixelRatio: 2, + system: 'iOS 11.0', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2018, + }, + { + name: 'iPhone 17 Air', + os: 'ios', + screen: { width: 420, height: 912 }, + pixelRatio: 3, + system: 'iOS 18.0', + statusBarHeight: 54, + safeAreaInsets: { top: 68, bottom: 34 }, + safeAreaInsetsLandscape: { left: 68, right: 68, bottom: 20 }, + cutout: { shape: 'pill', width: 125, height: 37, top: 20 }, + releaseYear: 2025, + }, + { + name: 'iPad Pro M4', + os: 'ios', + formFactor: 'tablet', + screen: { width: 834, height: 1210 }, + pixelRatio: 2, + system: 'iOS 17.5', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2024, + }, + { + name: 'iPad Air M2', + os: 'ios', + formFactor: 'tablet', + screen: { width: 820, height: 1180 }, + pixelRatio: 2, + system: 'iOS 17.4', + statusBarHeight: 24, + statusBarHeightLandscape: 24, + safeAreaInsets: { top: 24, bottom: 25 }, + safeAreaInsetsLandscape: { top: 24, bottom: 25 }, + shell: { screenRadius: 18 }, + releaseYear: 2024, + }, ] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9f1cc0..ddb34e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: eslint: specifier: ^10.2.1 version: 10.9.1(jiti@2.7.0) + oxfmt: + specifier: 0.67.0 + version: 0.67.0 + oxlint: + specifier: 1.82.0 + version: 1.82.0 typescript: specifier: 5.9.2 version: 5.9.2 @@ -334,6 +340,234 @@ packages: '@oxc-project/types@0.148.0': resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} + '@oxfmt/binding-android-arm-eabi@0.67.0': + resolution: {integrity: sha512-2olh3ioEmc4gRzQm7jxyB1b/PFBoFvTq8KdgYySeNpysDtA6DEg2Mvya4/I6flhL7G0eOrE8RD7JCNCIMhE16Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.67.0': + resolution: {integrity: sha512-ulfw8EHN1MBq/MFFDXw2/M1VAFu5mRUcnuZ8Hqbv9viAnFzO9t1jKSAsDqKYYDGMlytF/uj6Z5z5n/tHupnKhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.67.0': + resolution: {integrity: sha512-MfONZx/O2o9M5v2jDFol556G9+A+P9xCuJ4DZ+qhE+RnaCdoscy6Eu5nq1dbuNxhwdJyZ6kLI7fnG9mwEeOeGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.67.0': + resolution: {integrity: sha512-CYnIx5LvFVJnyJcCqwH2jxMKjFjqo5678MPjdmNFoSGMhlOvZ/xRZqvhDcolKrXc8fezW3AKh+C4wyoFuWOSSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.67.0': + resolution: {integrity: sha512-7/iF1orvIS9mxhKUqnmtMgm+OrSQ5acPwuvdQrm6ECgqbwPmC+Pw9cdke3sNfVN6pT2hbJ58+jP8BCThl5HXOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + resolution: {integrity: sha512-yy+OGys07IZOpOmYPZoObKyUQLkfxeQqeCypk+1jaZd8HGo77hzvU1Jg8X3+W75o+9lszOjBfg0nkGtlwYywXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + resolution: {integrity: sha512-wPIeeigXgJpwNw3wydYRt3U9iN9Y/ejpOZuYL9IA7igxWs7LIQMOkhKxTumRvy6dIv0iXKk3RTw3Vmjg0i+2sg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.67.0': + resolution: {integrity: sha512-0+XNxcdbkTfxdcD4qW6Ci9n+mBNJ8xTBumnxKvKBmRFOdx0Wf8/KiHjCJayooXmYkqRpRVd98Q5egvzx5BLSgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-arm64-musl@0.67.0': + resolution: {integrity: sha512-I75LKPJyNOYUzkqAiAMIE31+Ye7xtQXZdoty1IXn4B+bw5Zpmez5wfG19ejGpNnS/BzQ7LFS+7jxuTPb+vHiZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + resolution: {integrity: sha512-c2M5iRpe1QMZSRE/UvZoPdXBWb5Ic/ycvOyNiKCqPwQ/OyOKIMiJs02ynlNnjb7ZZJnRXYLmGcohoINOcwDK3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + resolution: {integrity: sha512-dQzzYlV24Udhfm5ECuSdgqRvFJU/CGHzcYYEO3dLM6W6+CHiBFrq9OjIllkdCcPhsoSQ8o223Dja84MOSzed9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-musl@0.67.0': + resolution: {integrity: sha512-rFNq1CgX4qMJANOq42LkAs90JE80GpiaEohAV2qn/gT2hGjQTW1zBO5zQBxArI4926pM1OSzo3CN0tBszGBIaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-s390x-gnu@0.67.0': + resolution: {integrity: sha512-Sky6rEdz2o5IGq01lPhS12yEvDdChVEcaYrcLHkveh4Fx0qPjljE/Iul6SX/bRMl6lNc8J7J/mDQdzgBdA++Pg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxfmt/binding-linux-x64-gnu@0.67.0': + resolution: {integrity: sha512-vPXmlNORV8AZq2Ocxh07pxwMjfENUWCV/eZArnao0qC3NO/hDeTVkQvee7SJJUbIiF5PZbBa4kYmaXnu7Rk58w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-linux-x64-musl@0.67.0': + resolution: {integrity: sha512-x/WAtFqYtVr3vZ9ni8nr4kn9whSitg8fOljq/pZzBpxopRdY1BMLZCZkrbIbaBcYkm46qGbqVea2FCWmtQ2P9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-openharmony-arm64@0.67.0': + resolution: {integrity: sha512-eRw9Neh4/aA6i+q/R3WU1gGQINhVM0J4fXIm6t27caOamkr/37uAkp1IdBx4zlJH97hmXR63z/q9n5c5dN7MzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.67.0': + resolution: {integrity: sha512-YIMvb+sGNYN2uc6+QK2HLPeEKM2vl7QZ5onQzpAJRb6pnf0DwUFP5R8tdS9R0l8hdUil2gu4Uxd0Yxrop0iT4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.67.0': + resolution: {integrity: sha512-LzmU9MyACPzwNDIK0ItMedHPz735Ug7ELWguxo4/kuy6zWuDoeglOAEFCY8jLg0PzRpFO3hDyLFe2Gu2eFDeGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.67.0': + resolution: {integrity: sha512-sbQOIDNLUEeVZcAJcSL5VURn7kfjvilPviody4Yl5n8lQCDtUm+C9oHTTwZS/m4d/Z6Vv3jNEiAofH932NPPCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.82.0': + resolution: {integrity: sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.82.0': + resolution: {integrity: sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.82.0': + resolution: {integrity: sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.82.0': + resolution: {integrity: sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.82.0': + resolution: {integrity: sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + resolution: {integrity: sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.82.0': + resolution: {integrity: sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.82.0': + resolution: {integrity: sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.82.0': + resolution: {integrity: sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.82.0': + resolution: {integrity: sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.82.0': + resolution: {integrity: sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.82.0': + resolution: {integrity: sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.82.0': + resolution: {integrity: sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.82.0': + resolution: {integrity: sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.82.0': + resolution: {integrity: sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.82.0': + resolution: {integrity: sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.82.0': + resolution: {integrity: sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.82.0': + resolution: {integrity: sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.82.0': + resolution: {integrity: sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/binding-android-arm-eabi@1.2.7': resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1393,6 +1627,32 @@ packages: resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} + oxfmt@0.67.0: + resolution: {integrity: sha512-vV7sSiPsaO0mSxdoUdayipVDFPzW/UQ+hrezEHa20+Tx1dnMdZLSRHMT0PdS67FFbhd74M1n08asW21aLGeCrA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint@1.82.0: + resolution: {integrity: sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1607,6 +1867,10 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.1.1: resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} @@ -2061,6 +2325,120 @@ snapshots: '@oxc-project/types@0.148.0': {} + '@oxfmt/binding-android-arm-eabi@0.67.0': + optional: true + + '@oxfmt/binding-android-arm64@0.67.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.67.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.67.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.67.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.67.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.67.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.67.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.67.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.67.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.67.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.82.0': + optional: true + + '@oxlint/binding-android-arm64@1.82.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.82.0': + optional: true + + '@oxlint/binding-darwin-x64@1.82.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.82.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.82.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.82.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.82.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.82.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.82.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.82.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.82.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.82.0': + optional: true + '@rolldown/binding-android-arm-eabi@1.2.7': optional: true @@ -3252,6 +3630,52 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxfmt@0.67.0: + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.67.0 + '@oxfmt/binding-android-arm64': 0.67.0 + '@oxfmt/binding-darwin-arm64': 0.67.0 + '@oxfmt/binding-darwin-x64': 0.67.0 + '@oxfmt/binding-freebsd-x64': 0.67.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.67.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.67.0 + '@oxfmt/binding-linux-arm64-gnu': 0.67.0 + '@oxfmt/binding-linux-arm64-musl': 0.67.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-musl': 0.67.0 + '@oxfmt/binding-linux-s390x-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-musl': 0.67.0 + '@oxfmt/binding-openharmony-arm64': 0.67.0 + '@oxfmt/binding-win32-arm64-msvc': 0.67.0 + '@oxfmt/binding-win32-ia32-msvc': 0.67.0 + '@oxfmt/binding-win32-x64-msvc': 0.67.0 + + oxlint@1.82.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.82.0 + '@oxlint/binding-android-arm64': 1.82.0 + '@oxlint/binding-darwin-arm64': 1.82.0 + '@oxlint/binding-darwin-x64': 1.82.0 + '@oxlint/binding-freebsd-x64': 1.82.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.82.0 + '@oxlint/binding-linux-arm-musleabihf': 1.82.0 + '@oxlint/binding-linux-arm64-gnu': 1.82.0 + '@oxlint/binding-linux-arm64-musl': 1.82.0 + '@oxlint/binding-linux-ppc64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-musl': 1.82.0 + '@oxlint/binding-linux-s390x-gnu': 1.82.0 + '@oxlint/binding-linux-x64-gnu': 1.82.0 + '@oxlint/binding-linux-x64-musl': 1.82.0 + '@oxlint/binding-openharmony-arm64': 1.82.0 + '@oxlint/binding-win32-arm64-msvc': 1.82.0 + '@oxlint/binding-win32-ia32-msvc': 1.82.0 + '@oxlint/binding-win32-x64-msvc': 1.82.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -3528,6 +3952,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 + tinypool@2.1.2: {} + tinyrainbow@3.1.1: {} tldts-core@7.4.11: {}