macOS: load device model names from dataset
This commit is contained in:
@@ -101,6 +101,7 @@ pnpm clawdis gateway --force
|
||||
### macOS Companion (Clawdis.app)
|
||||
|
||||
- A menu bar app that can start/stop the Gateway, show health/presence, and provide a local ops UI.
|
||||
- Instances UI shows friendly hardware model names (from the vendored MIT dataset under `apps/macos/Sources/Clawdis/Resources/DeviceModels/`).
|
||||
- **Voice Wake** (on-device speech recognition) and Push-to-talk overlay.
|
||||
- **WebChat** embed + debug tooling (logs, status, heartbeats, sessions).
|
||||
- Hosts **PeekabooBridge** for UI automation brokering (for clawd workflows).
|
||||
|
||||
@@ -50,8 +50,9 @@ let package = Package(
|
||||
],
|
||||
resources: [
|
||||
.copy("Resources/Clawdis.icns"),
|
||||
.copy("Resources/WebChat"),
|
||||
.copy("Resources/CanvasA2UI"),
|
||||
.copy("Resources/WebChat"),
|
||||
.copy("Resources/DeviceModels"),
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
|
||||
@@ -6,15 +6,18 @@ struct DevicePresentation: Sendable {
|
||||
}
|
||||
|
||||
enum DeviceModelCatalog {
|
||||
private static let modelIdentifierToName: [String: String] = loadModelIdentifierToName()
|
||||
|
||||
static func presentation(deviceFamily: String?, modelIdentifier: String?) -> DevicePresentation? {
|
||||
let family = (deviceFamily ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let model = (modelIdentifier ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
let modelEntry = model.isEmpty ? nil : modelIdentifierTable[model]
|
||||
let symbol = modelEntry?.symbol ?? fallbackSymbol(for: family, modelIdentifier: model)
|
||||
let friendlyName = model.isEmpty ? nil : modelIdentifierToName[model]
|
||||
let symbol = symbolFor(modelIdentifier: model, friendlyName: friendlyName)
|
||||
?? fallbackSymbol(for: family, modelIdentifier: model)
|
||||
|
||||
let title = if let name = modelEntry?.name, !name.isEmpty {
|
||||
name
|
||||
let title = if let friendlyName, !friendlyName.isEmpty {
|
||||
friendlyName
|
||||
} else if !family.isEmpty, !model.isEmpty {
|
||||
"\(family) (\(model))"
|
||||
} else if !family.isEmpty {
|
||||
@@ -29,6 +32,36 @@ enum DeviceModelCatalog {
|
||||
return DevicePresentation(title: title, symbol: symbol)
|
||||
}
|
||||
|
||||
private static func symbolFor(modelIdentifier rawModelIdentifier: String, friendlyName: String?) -> String? {
|
||||
let modelIdentifier = rawModelIdentifier.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !modelIdentifier.isEmpty else { return nil }
|
||||
|
||||
let lower = modelIdentifier.lowercased()
|
||||
if lower.hasPrefix("ipad") { return "ipad" }
|
||||
if lower.hasPrefix("iphone") { return "iphone" }
|
||||
if lower.hasPrefix("ipod") { return "iphone" }
|
||||
if lower.hasPrefix("watch") { return "applewatch" }
|
||||
if lower.hasPrefix("appletv") { return "appletv" }
|
||||
if lower.hasPrefix("audio") || lower.hasPrefix("homepod") { return "speaker" }
|
||||
|
||||
if lower.hasPrefix("macbook") || lower.hasPrefix("macbookpro") || lower.hasPrefix("macbookair") {
|
||||
return "laptopcomputer"
|
||||
}
|
||||
if lower.hasPrefix("imac") || lower.hasPrefix("macmini") || lower.hasPrefix("macpro") || lower.hasPrefix("macstudio") {
|
||||
return "desktopcomputer"
|
||||
}
|
||||
|
||||
if lower.hasPrefix("mac"), let friendlyNameLower = friendlyName?.lowercased() {
|
||||
if friendlyNameLower.contains("macbook") { return "laptopcomputer" }
|
||||
if friendlyNameLower.contains("imac") { return "desktopcomputer" }
|
||||
if friendlyNameLower.contains("mac mini") { return "desktopcomputer" }
|
||||
if friendlyNameLower.contains("mac studio") { return "desktopcomputer" }
|
||||
if friendlyNameLower.contains("mac pro") { return "desktopcomputer" }
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func fallbackSymbol(for familyRaw: String, modelIdentifier: String) -> String? {
|
||||
let family = familyRaw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if family.isEmpty { return nil }
|
||||
@@ -49,21 +82,62 @@ enum DeviceModelCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
private struct ModelEntry: Sendable {
|
||||
let name: String
|
||||
let symbol: String?
|
||||
private static func loadModelIdentifierToName() -> [String: String] {
|
||||
var combined: [String: String] = [:]
|
||||
combined.merge(loadMapping(resourceName: "ios-device-identifiers"), uniquingKeysWith: { current, _ in current })
|
||||
combined.merge(loadMapping(resourceName: "mac-device-identifiers"), uniquingKeysWith: { current, _ in current })
|
||||
return combined
|
||||
}
|
||||
|
||||
// Friendly model names for a small set of known identifiers.
|
||||
// Extend this table as needed; unknown identifiers fall back to the raw value.
|
||||
private static let modelIdentifierTable: [String: ModelEntry] = [
|
||||
// iPad
|
||||
"iPad16,5": .init(name: "iPad Pro 11-inch (M4)", symbol: "ipad"),
|
||||
"iPad16,6": .init(name: "iPad Pro 13-inch (M4)", symbol: "ipad"),
|
||||
private static func loadMapping(resourceName: String) -> [String: String] {
|
||||
guard let url = Bundle.module.url(
|
||||
forResource: resourceName,
|
||||
withExtension: "json",
|
||||
subdirectory: "DeviceModels")
|
||||
else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
// Mac
|
||||
"Mac16,6": .init(name: "MacBook Pro (14-inch, 2024)", symbol: "laptopcomputer"),
|
||||
"Mac16,8": .init(name: "MacBook Pro (16-inch, 2024)", symbol: "laptopcomputer"),
|
||||
]
|
||||
do {
|
||||
let data = try Data(contentsOf: url)
|
||||
let decoded = try JSONDecoder().decode([String: NameValue].self, from: data)
|
||||
return decoded.compactMapValues { $0.normalizedName }
|
||||
} catch {
|
||||
return [:]
|
||||
}
|
||||
}
|
||||
|
||||
private enum NameValue: Decodable {
|
||||
case string(String)
|
||||
case stringArray([String])
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
if let s = try? container.decode(String.self) {
|
||||
self = .string(s)
|
||||
return
|
||||
}
|
||||
if let arr = try? container.decode([String].self) {
|
||||
self = .stringArray(arr)
|
||||
return
|
||||
}
|
||||
throw DecodingError.typeMismatch(
|
||||
String.self,
|
||||
.init(codingPath: decoder.codingPath, debugDescription: "Expected string or string array"))
|
||||
}
|
||||
|
||||
var normalizedName: String? {
|
||||
switch self {
|
||||
case .string(let s):
|
||||
let trimmed = s.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
case .stringArray(let arr):
|
||||
let values = arr
|
||||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
.filter { !$0.isEmpty }
|
||||
guard !values.isEmpty else { return nil }
|
||||
return values.joined(separator: " / ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ enum InstanceIdentity {
|
||||
var buffer = [CChar](repeating: 0, count: size)
|
||||
guard sysctlbyname("hw.model", &buffer, &size, nil, 0) == 0 else { return nil }
|
||||
|
||||
let s = String(cString: buffer).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let bytes = buffer.prefix { $0 != 0 }.map { UInt8(bitPattern: $0) }
|
||||
let s = String(decoding: bytes, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return s.isEmpty ? nil : s
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Kyle Seongwoo Jun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Apple device identifier mappings
|
||||
|
||||
This directory includes model identifier → human-readable name mappings derived from the open-source project:
|
||||
|
||||
- `kyle-seongwoo-jun/apple-device-identifiers`
|
||||
- iOS mapping pinned to commit `8e7388b29da046183f5d976eb74dbb2f2acda955`
|
||||
- macOS mapping pinned to commit `98ca75324f7a88c1649eb5edfc266ef47b7b8193`
|
||||
|
||||
See `LICENSE.apple-device-identifiers.txt` for license terms.
|
||||
@@ -0,0 +1,176 @@
|
||||
{
|
||||
"i386": "iPhone Simulator",
|
||||
"x86_64": "iPhone Simulator",
|
||||
"arm64": "iPhone Simulator",
|
||||
"iPhone1,1": "iPhone",
|
||||
"iPhone1,2": "iPhone 3G",
|
||||
"iPhone2,1": "iPhone 3GS",
|
||||
"iPhone3,1": "iPhone 4",
|
||||
"iPhone3,2": "iPhone 4",
|
||||
"iPhone3,3": "iPhone 4",
|
||||
"iPhone4,1": "iPhone 4s",
|
||||
"iPhone5,1": "iPhone 5",
|
||||
"iPhone5,2": "iPhone 5",
|
||||
"iPhone5,3": "iPhone 5c",
|
||||
"iPhone5,4": "iPhone 5c",
|
||||
"iPhone6,1": "iPhone 5s",
|
||||
"iPhone6,2": "iPhone 5s",
|
||||
"iPhone7,1": "iPhone 6 Plus",
|
||||
"iPhone7,2": "iPhone 6",
|
||||
"iPhone8,1": "iPhone 6s",
|
||||
"iPhone8,2": "iPhone 6s Plus",
|
||||
"iPhone8,4": "iPhone SE (1st generation)",
|
||||
"iPhone9,1": "iPhone 7",
|
||||
"iPhone9,2": "iPhone 7 Plus",
|
||||
"iPhone9,3": "iPhone 7",
|
||||
"iPhone9,4": "iPhone 7 Plus",
|
||||
"iPhone10,1": "iPhone 8",
|
||||
"iPhone10,2": "iPhone 8 Plus",
|
||||
"iPhone10,3": "iPhone X",
|
||||
"iPhone10,4": "iPhone 8",
|
||||
"iPhone10,5": "iPhone 8 Plus",
|
||||
"iPhone10,6": "iPhone X",
|
||||
"iPhone11,2": "iPhone XS",
|
||||
"iPhone11,4": "iPhone XS Max",
|
||||
"iPhone11,6": "iPhone XS Max",
|
||||
"iPhone11,8": "iPhone XR",
|
||||
"iPhone12,1": "iPhone 11",
|
||||
"iPhone12,3": "iPhone 11 Pro",
|
||||
"iPhone12,5": "iPhone 11 Pro Max",
|
||||
"iPhone12,8": "iPhone SE (2nd generation)",
|
||||
"iPhone13,1": "iPhone 12 mini",
|
||||
"iPhone13,2": "iPhone 12",
|
||||
"iPhone13,3": "iPhone 12 Pro",
|
||||
"iPhone13,4": "iPhone 12 Pro Max",
|
||||
"iPhone14,2": "iPhone 13 Pro",
|
||||
"iPhone14,3": "iPhone 13 Pro Max",
|
||||
"iPhone14,4": "iPhone 13 mini",
|
||||
"iPhone14,5": "iPhone 13",
|
||||
"iPhone14,6": "iPhone SE (3rd generation)",
|
||||
"iPhone14,7": "iPhone 14",
|
||||
"iPhone14,8": "iPhone 14 Plus",
|
||||
"iPhone15,2": "iPhone 14 Pro",
|
||||
"iPhone15,3": "iPhone 14 Pro Max",
|
||||
"iPhone15,4": "iPhone 15",
|
||||
"iPhone15,5": "iPhone 15 Plus",
|
||||
"iPhone16,1": "iPhone 15 Pro",
|
||||
"iPhone16,2": "iPhone 15 Pro Max",
|
||||
"iPhone17,1": "iPhone 16 Pro",
|
||||
"iPhone17,2": "iPhone 16 Pro Max",
|
||||
"iPhone17,3": "iPhone 16",
|
||||
"iPhone17,4": "iPhone 16 Plus",
|
||||
"iPhone17,5": "iPhone 16e",
|
||||
"iPhone18,1": "iPhone 17 Pro",
|
||||
"iPhone18,2": "iPhone 17 Pro Max",
|
||||
"iPhone18,3": "iPhone 17",
|
||||
"iPhone18,4": "iPhone Air",
|
||||
"iPad1,1": "iPad",
|
||||
"iPad1,2": "iPad",
|
||||
"iPad2,1": "iPad 2",
|
||||
"iPad2,2": "iPad 2",
|
||||
"iPad2,3": "iPad 2",
|
||||
"iPad2,4": "iPad 2",
|
||||
"iPad2,5": "iPad mini",
|
||||
"iPad2,6": "iPad mini",
|
||||
"iPad2,7": "iPad mini",
|
||||
"iPad3,1": "iPad (3rd generation)",
|
||||
"iPad3,2": "iPad (3rd generation)",
|
||||
"iPad3,3": "iPad (3rd generation)",
|
||||
"iPad3,4": "iPad (4th generation)",
|
||||
"iPad3,5": "iPad (4th generation)",
|
||||
"iPad3,6": "iPad (4th generation)",
|
||||
"iPad4,1": "iPad Air",
|
||||
"iPad4,2": "iPad Air",
|
||||
"iPad4,3": "iPad Air",
|
||||
"iPad4,4": "iPad mini 2",
|
||||
"iPad4,5": "iPad mini 2",
|
||||
"iPad4,6": "iPad mini 2",
|
||||
"iPad4,7": "iPad mini 3",
|
||||
"iPad4,8": "iPad mini 3",
|
||||
"iPad4,9": "iPad mini 3",
|
||||
"iPad5,1": "iPad mini 4",
|
||||
"iPad5,2": "iPad mini 4",
|
||||
"iPad5,3": "iPad Air 2",
|
||||
"iPad5,4": "iPad Air 2",
|
||||
"iPad6,3": "iPad Pro (9.7-inch)",
|
||||
"iPad6,4": "iPad Pro (9.7-inch)",
|
||||
"iPad6,7": "iPad Pro (12.9-inch)",
|
||||
"iPad6,8": "iPad Pro (12.9-inch)",
|
||||
"iPad6,11": "iPad (5th generation)",
|
||||
"iPad6,12": "iPad (5th generation)",
|
||||
"iPad7,1": "iPad Pro (12.9-inch) (2nd generation)",
|
||||
"iPad7,2": "iPad Pro (12.9-inch) (2nd generation)",
|
||||
"iPad7,3": "iPad Pro (10.5-inch)",
|
||||
"iPad7,4": "iPad Pro (10.5-inch)",
|
||||
"iPad7,5": "iPad (6th generation)",
|
||||
"iPad7,6": "iPad (6th generation)",
|
||||
"iPad7,11": "iPad (7th generation)",
|
||||
"iPad7,12": "iPad (7th generation)",
|
||||
"iPad8,1": "iPad Pro (11-inch)",
|
||||
"iPad8,2": "iPad Pro (11-inch)",
|
||||
"iPad8,3": "iPad Pro (11-inch)",
|
||||
"iPad8,4": "iPad Pro (11-inch)",
|
||||
"iPad8,5": "iPad Pro (12.9-inch) (3rd generation)",
|
||||
"iPad8,6": "iPad Pro (12.9-inch) (3rd generation)",
|
||||
"iPad8,7": "iPad Pro (12.9-inch) (3rd generation)",
|
||||
"iPad8,8": "iPad Pro (12.9-inch) (3rd generation)",
|
||||
"iPad8,9": "iPad Pro (11-inch) (2nd generation)",
|
||||
"iPad8,10": "iPad Pro (11-inch) (2nd generation)",
|
||||
"iPad8,11": "iPad Pro (12.9-inch) (4th generation)",
|
||||
"iPad8,12": "iPad Pro (12.9-inch) (4th generation)",
|
||||
"iPad11,1": "iPad mini (5th generation)",
|
||||
"iPad11,2": "iPad mini (5th generation)",
|
||||
"iPad11,3": "iPad Air (3rd generation)",
|
||||
"iPad11,4": "iPad Air (3rd generation)",
|
||||
"iPad11,6": "iPad (8th generation)",
|
||||
"iPad11,7": "iPad (8th generation)",
|
||||
"iPad12,1": "iPad (9th generation)",
|
||||
"iPad12,2": "iPad (9th generation)",
|
||||
"iPad13,1": "iPad Air (4th generation)",
|
||||
"iPad13,2": "iPad Air (4th generation)",
|
||||
"iPad13,4": "iPad Pro (11-inch) (3rd generation)",
|
||||
"iPad13,5": "iPad Pro (11-inch) (3rd generation)",
|
||||
"iPad13,6": "iPad Pro (11-inch) (3rd generation)",
|
||||
"iPad13,7": "iPad Pro (11-inch) (3rd generation)",
|
||||
"iPad13,8": "iPad Pro (12.9-inch) (5th generation)",
|
||||
"iPad13,9": "iPad Pro (12.9-inch) (5th generation)",
|
||||
"iPad13,10": "iPad Pro (12.9-inch) (5th generation)",
|
||||
"iPad13,11": "iPad Pro (12.9-inch) (5th generation)",
|
||||
"iPad13,16": "iPad Air (5th generation)",
|
||||
"iPad13,17": "iPad Air (5th generation)",
|
||||
"iPad13,18": "iPad (10th generation)",
|
||||
"iPad13,19": "iPad (10th generation)",
|
||||
"iPad14,1": "iPad mini (6th generation)",
|
||||
"iPad14,2": "iPad mini (6th generation)",
|
||||
"iPad14,3": "iPad Pro (11-inch) (4th generation)",
|
||||
"iPad14,4": "iPad Pro (11-inch) (4th generation)",
|
||||
"iPad14,5": "iPad Pro (12.9-inch) (6th generation)",
|
||||
"iPad14,6": "iPad Pro (12.9-inch) (6th generation)",
|
||||
"iPad14,8": "iPad Air 11-inch (M2)",
|
||||
"iPad14,9": "iPad Air 11-inch (M2)",
|
||||
"iPad14,10": "iPad Air 13-inch (M2)",
|
||||
"iPad14,11": "iPad Air 13-inch (M2)",
|
||||
"iPad15,3": "iPad Air 11-inch (M3)",
|
||||
"iPad15,4": "iPad Air 11-inch (M3)",
|
||||
"iPad15,5": "iPad Air 13-inch (M3)",
|
||||
"iPad15,6": "iPad Air 13-inch (M3)",
|
||||
"iPad15,7": "iPad (A16)",
|
||||
"iPad15,8": "iPad (A16)",
|
||||
"iPad16,1": "iPad mini (A17 Pro)",
|
||||
"iPad16,2": "iPad mini (A17 Pro)",
|
||||
"iPad16,3": "iPad Pro 11-inch (M4)",
|
||||
"iPad16,4": "iPad Pro 11-inch (M4)",
|
||||
"iPad16,5": "iPad Pro 13-inch (M4)",
|
||||
"iPad16,6": "iPad Pro 13-inch (M4)",
|
||||
"iPad17,1": "iPad Pro 11-inch (M5)",
|
||||
"iPad17,2": "iPad Pro 11-inch (M5)",
|
||||
"iPad17,3": "iPad Pro 13-inch (M5)",
|
||||
"iPad17,4": "iPad Pro 13-inch (M5)",
|
||||
"iPod1,1": "iPod touch",
|
||||
"iPod2,1": "iPod touch (2nd generation)",
|
||||
"iPod3,1": "iPod touch (3rd generation)",
|
||||
"iPod4,1": "iPod touch (4th generation)",
|
||||
"iPod5,1": "iPod touch (5th generation)",
|
||||
"iPod7,1": "iPod touch (6th generation)",
|
||||
"iPod9,1": "iPod touch (7th generation)"
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
{
|
||||
"iMac9,1": [
|
||||
"iMac (20-inch, Early 2009)",
|
||||
"iMac (24-inch, Early 2009)"
|
||||
],
|
||||
"iMac10,1": [
|
||||
"iMac (21.5-inch, Late 2009)",
|
||||
"iMac (27-inch, Late 2009)"
|
||||
],
|
||||
"iMac11,2": "iMac (21.5-inch, Mid 2010)",
|
||||
"iMac11,3": "iMac (27-inch, Mid 2010)",
|
||||
"iMac12,1": "iMac (21.5-inch, Mid 2011)",
|
||||
"iMac12,2": "iMac (27-inch, Mid 2011)",
|
||||
"iMac13,1": "iMac (21.5-inch, Late 2012)",
|
||||
"iMac13,2": "iMac (27-inch, Late 2012)",
|
||||
"iMac14,1": "iMac (21.5-inch, Late 2013)",
|
||||
"iMac14,2": "iMac (27-inch, Late 2013)",
|
||||
"iMac14,4": "iMac (21.5-inch, Mid 2014)",
|
||||
"iMac15,1": [
|
||||
"iMac (Retina 5K, 27-inch, Late 2014)",
|
||||
"iMac (Retina 5K, 27-inch, Mid 2015)"
|
||||
],
|
||||
"iMac16,1": "iMac (21.5-inch, Late 2015)",
|
||||
"iMac16,2": "iMac (Retina 4K, 21.5-inch, Late 2015)",
|
||||
"iMac17,1": "iMac (Retina 5K, 27-inch, Late 2015)",
|
||||
"iMac18,1": "iMac (21.5-inch, 2017)",
|
||||
"iMac18,2": "iMac (Retina 4K, 21.5-inch, 2017)",
|
||||
"iMac18,3": "iMac (Retina 5K, 27-inch, 2017)",
|
||||
"iMac19,1": "iMac (Retina 5K, 27-inch, 2019)",
|
||||
"iMac19,2": "iMac (Retina 4K, 21.5-inch, 2019)",
|
||||
"iMac20,1": "iMac (Retina 5K, 27-inch, 2020)",
|
||||
"iMac20,2": "iMac (Retina 5K, 27-inch, 2020)",
|
||||
"iMac21,1": "iMac (24-inch, M1, 2021)",
|
||||
"iMac21,2": "iMac (24-inch, M1, 2021)",
|
||||
"iMacPro1,1": "iMac Pro (2017)",
|
||||
"Mac13,1": "Mac Studio (2022)",
|
||||
"Mac13,2": "Mac Studio (2022)",
|
||||
"Mac14,2": "MacBook Air (M2, 2022)",
|
||||
"Mac14,3": "Mac mini (2023)",
|
||||
"Mac14,5": "MacBook Pro (14-inch, 2023)",
|
||||
"Mac14,6": "MacBook Pro (16-inch, 2023)",
|
||||
"Mac14,7": "MacBook Pro (13-inch, M2, 2022)",
|
||||
"Mac14,8": [
|
||||
"Mac Pro (2023)",
|
||||
"Mac Pro (Rack, 2023)"
|
||||
],
|
||||
"Mac14,9": "MacBook Pro (14-inch, 2023)",
|
||||
"Mac14,10": "MacBook Pro (16-inch, 2023)",
|
||||
"Mac14,12": "Mac mini (2023)",
|
||||
"Mac14,13": "Mac Studio (2023)",
|
||||
"Mac14,14": "Mac Studio (2023)",
|
||||
"Mac14,15": "MacBook Air (15-inch, M2, 2023)",
|
||||
"Mac15,3": "MacBook Pro (14-inch, Nov 2023)",
|
||||
"Mac15,4": "iMac (24-inch, 2023, Two ports)",
|
||||
"Mac15,5": "iMac (24-inch, 2023, Four ports)",
|
||||
"Mac15,6": "MacBook Pro (14-inch, Nov 2023)",
|
||||
"Mac15,7": "MacBook Pro (16-inch, Nov 2023)",
|
||||
"Mac15,8": "MacBook Pro (14-inch, Nov 2023)",
|
||||
"Mac15,9": "MacBook Pro (16-inch, Nov 2023)",
|
||||
"Mac15,10": "MacBook Pro (14-inch, Nov 2023)",
|
||||
"Mac15,11": "MacBook Pro (16-inch, Nov 2023)",
|
||||
"Mac15,12": "MacBook Air (13-inch, M3, 2024)",
|
||||
"Mac15,13": "MacBook Air (15-inch, M3, 2024)",
|
||||
"Mac15,14": "Mac Studio (2025)",
|
||||
"Mac16,1": "MacBook Pro (14-inch, 2024)",
|
||||
"Mac16,2": "iMac (24-inch, 2024, Two ports)",
|
||||
"Mac16,3": "iMac (24-inch, 2024, Four ports)",
|
||||
"Mac16,5": "MacBook Pro (16-inch, 2024)",
|
||||
"Mac16,6": "MacBook Pro (14-inch, 2024)",
|
||||
"Mac16,7": "MacBook Pro (16-inch, 2024)",
|
||||
"Mac16,8": "MacBook Pro (14-inch, 2024)",
|
||||
"Mac16,9": "Mac Studio (2025)",
|
||||
"Mac16,10": "Mac mini (2024)",
|
||||
"Mac16,11": "Mac mini (2024)",
|
||||
"Mac16,12": "MacBook Air (13-inch, M4, 2025)",
|
||||
"Mac16,13": "MacBook Air (15-inch, M4, 2025)",
|
||||
"Mac17,2": "MacBook Pro (14-inch, M5)",
|
||||
"MacBook5,2": [
|
||||
"MacBook (13-inch, Early 2009)",
|
||||
"MacBook (13-inch, Mid 2009)"
|
||||
],
|
||||
"MacBook6,1": "MacBook (13-inch, Late 2009)",
|
||||
"MacBook7,1": "MacBook (13-inch, Mid 2010)",
|
||||
"MacBook8,1": "MacBook (Retina, 12-inch, Early 2015)",
|
||||
"MacBook9,1": "MacBook (Retina, 12-inch, Early 2016)",
|
||||
"MacBook10,1": "MacBook (Retina, 12-inch, 2017)",
|
||||
"MacBookAir2,1": "MacBook Air (Mid 2009)",
|
||||
"MacBookAir3,1": "MacBook Air (11-inch, Late 2010)",
|
||||
"MacBookAir3,2": "MacBook Air (13-inch, Late 2010)",
|
||||
"MacBookAir4,1": "MacBook Air (11-inch, Mid 2011)",
|
||||
"MacBookAir4,2": "MacBook Air (13-inch, Mid 2011)",
|
||||
"MacBookAir5,1": "MacBook Air (11-inch, Mid 2012)",
|
||||
"MacBookAir5,2": "MacBook Air (13-inch, Mid 2012)",
|
||||
"MacBookAir6,1": [
|
||||
"MacBook Air (11-inch, Early 2014)",
|
||||
"MacBook Air (11-inch, Mid 2013)"
|
||||
],
|
||||
"MacBookAir6,2": [
|
||||
"MacBook Air (13-inch, Early 2014)",
|
||||
"MacBook Air (13-inch, Mid 2013)"
|
||||
],
|
||||
"MacBookAir7,1": "MacBook Air (11-inch, Early 2015)",
|
||||
"MacBookAir7,2": [
|
||||
"MacBook Air (13-inch, 2017)",
|
||||
"MacBook Air (13-inch, Early 2015)"
|
||||
],
|
||||
"MacBookAir8,1": "MacBook Air (Retina, 13-inch, 2018)",
|
||||
"MacBookAir8,2": "MacBook Air (Retina, 13-inch, 2019)",
|
||||
"MacBookAir9,1": "MacBook Air (Retina, 13-inch, 2020)",
|
||||
"MacBookAir10,1": "MacBook Air (M1, 2020)",
|
||||
"MacBookPro4,1": [
|
||||
"MacBook Pro (15-inch, Early 2008)",
|
||||
"MacBook Pro (17-inch, Early 2008)"
|
||||
],
|
||||
"MacBookPro5,1": "MacBook Pro (15-inch, Late 2008)",
|
||||
"MacBookPro5,2": [
|
||||
"MacBook Pro (17-inch, Early 2009)",
|
||||
"MacBook Pro (17-inch, Mid 2009)"
|
||||
],
|
||||
"MacBookPro5,3": [
|
||||
"MacBook Pro (15-inch, 2.53GHz, Mid 2009)",
|
||||
"MacBook Pro (15-inch, Mid 2009)"
|
||||
],
|
||||
"MacBookPro5,5": "MacBook Pro (13-inch, Mid 2009)",
|
||||
"MacBookPro6,1": "MacBook Pro (17-inch, Mid 2010)",
|
||||
"MacBookPro6,2": "MacBook Pro (15-inch, Mid 2010)",
|
||||
"MacBookPro7,1": "MacBook Pro (13-inch, Mid 2010)",
|
||||
"MacBookPro8,1": [
|
||||
"MacBook Pro (13-inch, Early 2011)",
|
||||
"MacBook Pro (13-inch, Late 2011)"
|
||||
],
|
||||
"MacBookPro8,2": [
|
||||
"MacBook Pro (15-inch, Early 2011)",
|
||||
"MacBook Pro (15-inch, Late 2011)"
|
||||
],
|
||||
"MacBookPro8,3": [
|
||||
"MacBook Pro (17-inch, Early 2011)",
|
||||
"MacBook Pro (17-inch, Late 2011)"
|
||||
],
|
||||
"MacBookPro9,1": "MacBook Pro (15-inch, Mid 2012)",
|
||||
"MacBookPro9,2": "MacBook Pro (13-inch, Mid 2012)",
|
||||
"MacBookPro10,1": [
|
||||
"MacBook Pro (Retina, 15-inch, Early 2013)",
|
||||
"MacBook Pro (Retina, 15-inch, Mid 2012)"
|
||||
],
|
||||
"MacBookPro10,2": [
|
||||
"MacBook Pro (Retina, 13-inch, Early 2013)",
|
||||
"MacBook Pro (Retina, 13-inch, Late 2012)"
|
||||
],
|
||||
"MacBookPro11,1": [
|
||||
"MacBook Pro (Retina, 13-inch, Late 2013)",
|
||||
"MacBook Pro (Retina, 13-inch, Mid 2014)"
|
||||
],
|
||||
"MacBookPro11,2": [
|
||||
"MacBook Pro (Retina, 15-inch, Late 2013)",
|
||||
"MacBook Pro (Retina, 15-inch, Mid 2014)"
|
||||
],
|
||||
"MacBookPro11,3": [
|
||||
"MacBook Pro (Retina, 15-inch, Late 2013)",
|
||||
"MacBook Pro (Retina, 15-inch, Mid 2014)"
|
||||
],
|
||||
"MacBookPro11,4": "MacBook Pro (Retina, 15-inch, Mid 2015)",
|
||||
"MacBookPro11,5": "MacBook Pro (Retina, 15-inch, Mid 2015)",
|
||||
"MacBookPro12,1": "MacBook Pro (Retina, 13-inch, Early 2015)",
|
||||
"MacBookPro13,1": "MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)",
|
||||
"MacBookPro13,2": "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)",
|
||||
"MacBookPro13,3": "MacBook Pro (15-inch, 2016)",
|
||||
"MacBookPro14,1": "MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)",
|
||||
"MacBookPro14,2": "MacBook Pro (13-inch, 2017, Four Thunderbolt 3 ports)",
|
||||
"MacBookPro14,3": "MacBook Pro (15-inch, 2017)",
|
||||
"MacBookPro15,1": [
|
||||
"MacBook Pro (15-inch, 2018)",
|
||||
"MacBook Pro (15-inch, 2019)"
|
||||
],
|
||||
"MacBookPro15,2": [
|
||||
"MacBook Pro (13-inch, 2018, Four Thunderbolt 3 ports)",
|
||||
"MacBook Pro (13-inch, 2019, Four Thunderbolt 3 ports)"
|
||||
],
|
||||
"MacBookPro15,3": "MacBook Pro (15-inch, 2019)",
|
||||
"MacBookPro15,4": "MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)",
|
||||
"MacBookPro16,1": "MacBook Pro (16-inch, 2019)",
|
||||
"MacBookPro16,2": "MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)",
|
||||
"MacBookPro16,3": "MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)",
|
||||
"MacBookPro16,4": "MacBook Pro (16-inch, 2019)",
|
||||
"MacBookPro17,1": "MacBook Pro (13-inch, M1, 2020)",
|
||||
"MacBookPro18,1": "MacBook Pro (16-inch, 2021)",
|
||||
"MacBookPro18,2": "MacBook Pro (16-inch, 2021)",
|
||||
"MacBookPro18,3": "MacBook Pro (14-inch, 2021)",
|
||||
"MacBookPro18,4": "MacBook Pro (14-inch, 2021)",
|
||||
"Macmini3,1": [
|
||||
"Mac mini (Early 2009)",
|
||||
"Mac mini (Late 2009)"
|
||||
],
|
||||
"Macmini4,1": "Mac mini (Mid 2010)",
|
||||
"Macmini5,1": "Mac mini (Mid 2011)",
|
||||
"Macmini5,2": "Mac mini (Mid 2011)",
|
||||
"Macmini6,1": "Mac mini (Late 2012)",
|
||||
"Macmini6,2": "Mac mini (Late 2012)",
|
||||
"Macmini7,1": "Mac mini (Late 2014)",
|
||||
"Macmini8,1": "Mac mini (2018)",
|
||||
"Macmini9,1": "Mac mini (M1, 2020)",
|
||||
"MacPro4,1": "Mac Pro (Early 2009)",
|
||||
"MacPro5,1": [
|
||||
"Mac Pro (Mid 2010)",
|
||||
"Mac Pro (Mid 2012)",
|
||||
"Mac Pro Server (Mid 2010)",
|
||||
"Mac Pro Server (Mid 2012)"
|
||||
],
|
||||
"MacPro6,1": "Mac Pro (Late 2013)",
|
||||
"MacPro7,1": [
|
||||
"Mac Pro (2019)",
|
||||
"Mac Pro (Rack, 2019)"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user