import Foundation import SwiftUI enum TaskSection: String, CaseIterable, Identifiable { case processes = "Processes" case performance = "Performance" case history = "App history" case startup = "Startup apps" case users = "Users" case details = "Details" case services = "Services" case hardware = "Hardware" case settings = "Settings" var id: String { rawValue } var icon: String { switch self { case .processes: "square.stack.3d.up" case .performance: "waveform.path.ecg" case .history: "clock.arrow.circlepath" case .startup: "gauge.open.with.lines.needle.33percent" case .users: "person.2" case .details: "list.bullet.rectangle" case .services: "gearshape.2" case .hardware: "bolt.horizontal.circle" case .settings: "gearshape" } } } struct PowerProfile: Identifiable, Hashable, Sendable { let voltageVolts: Double let currentAmps: Double var id: String { "\(voltageVolts)-\(currentAmps)" } var watts: Double { voltageVolts * currentAmps } } struct PowerAdapterSnapshot: Hashable, Sendable { var name = "Power adapter" var manufacturer = "Not reported" var serial = "" var ratedWatts = 0.0 var negotiatedVoltage = 0.0 var negotiatedCurrent = 0.0 var profiles: [PowerProfile] = [] var negotiatedWatts: Double { negotiatedVoltage * negotiatedCurrent } } struct BatterySnapshot: Hashable, Sendable { var isPresent = false var chargePercent = 0.0 var isCharging = false var isFullyCharged = false var externalPowerConnected = false var cycleCount = 0 var designCycleCount = 0 var currentCapacityMAh = 0.0 var fullChargeCapacityMAh = 0.0 var designCapacityMAh = 0.0 var voltageVolts = 0.0 var currentAmps = 0.0 var temperatureCelsius = 0.0 var timeRemainingMinutes: Int? var serial = "" var adapter: PowerAdapterSnapshot? var systemPowerWatts = 0.0 var healthPercent: Double { guard designCapacityMAh > 0 else { return 0 } return min(100, fullChargeCapacityMAh / designCapacityMAh * 100) } var batteryPowerWatts: Double { let watts = abs(voltageVolts * currentAmps) return watts.isFinite && watts <= 500 ? watts : 0 } } struct ConnectedHardwareDevice: Identifiable, Hashable, Sendable { let id: String let name: String let vendor: String let speed: String let currentAvailableMA: Int? let currentRequiredMA: Int? let depth: Int } struct HardwarePortSnapshot: Identifiable, Hashable, Sendable { let id: String let name: String let transport: String let maximumSpeed: String let isConnected: Bool let status: String let devices: [ConnectedHardwareDevice] } struct FanSnapshot: Identifiable, Hashable, Sendable { let id: Int let name: String let actualRPM: Double let minimumRPM: Double let maximumRPM: Double let targetRPM: Double let mode: String var isAutomatic: Bool { mode.localizedCaseInsensitiveContains("automatic") } } struct HardwareSnapshot: Hashable, Sendable { var battery = BatterySnapshot() var ports: [HardwarePortSnapshot] = [] var fans: [FanSnapshot] = [] var thermalState = "Nominal" var capturedAt = Date.distantPast } struct MacMemorySpecification: Hashable, Sendable { let chip: String let cpuCoreCount: Int? let bandwidthGBps: Double let isMaximum: Bool var displayValue: String { let bandwidth = bandwidthGBps.rounded() == bandwidthGBps ? String(format: "%.0f", bandwidthGBps) : String(format: "%.2f", bandwidthGBps) return "\(isMaximum ? "Up to " : "")\(bandwidth) GB/s bandwidth (Apple specification)" } } enum MemoryBandwidthCatalog { // Ordered from most-specific to broadest. Core-count entries distinguish // configurations where Apple ships one chip name with multiple memory buses. static let specifications: [MacMemorySpecification] = [ .init(chip: "M5 Max", cpuCoreCount: nil, bandwidthGBps: 614, isMaximum: true), .init(chip: "M5 Pro", cpuCoreCount: nil, bandwidthGBps: 307, isMaximum: true), .init(chip: "M5", cpuCoreCount: nil, bandwidthGBps: 153, isMaximum: false), .init(chip: "M4 Max", cpuCoreCount: 16, bandwidthGBps: 546, isMaximum: false), .init(chip: "M4 Max", cpuCoreCount: 14, bandwidthGBps: 410, isMaximum: false), .init(chip: "M4 Pro", cpuCoreCount: nil, bandwidthGBps: 273, isMaximum: false), .init(chip: "M4", cpuCoreCount: nil, bandwidthGBps: 120, isMaximum: false), .init(chip: "M3 Ultra", cpuCoreCount: nil, bandwidthGBps: 819, isMaximum: false), .init(chip: "M3 Max", cpuCoreCount: 16, bandwidthGBps: 400, isMaximum: false), .init(chip: "M3 Max", cpuCoreCount: 14, bandwidthGBps: 300, isMaximum: false), .init(chip: "M3 Pro", cpuCoreCount: nil, bandwidthGBps: 150, isMaximum: false), .init(chip: "M3", cpuCoreCount: nil, bandwidthGBps: 100, isMaximum: false), .init(chip: "M2 Ultra", cpuCoreCount: nil, bandwidthGBps: 800, isMaximum: false), .init(chip: "M2 Max", cpuCoreCount: nil, bandwidthGBps: 400, isMaximum: false), .init(chip: "M2 Pro", cpuCoreCount: nil, bandwidthGBps: 200, isMaximum: false), .init(chip: "M2", cpuCoreCount: nil, bandwidthGBps: 100, isMaximum: false), .init(chip: "M1 Ultra", cpuCoreCount: nil, bandwidthGBps: 800, isMaximum: false), .init(chip: "M1 Max", cpuCoreCount: nil, bandwidthGBps: 400, isMaximum: false), .init(chip: "M1 Pro", cpuCoreCount: nil, bandwidthGBps: 200, isMaximum: false), .init(chip: "M1", cpuCoreCount: nil, bandwidthGBps: 68.25, isMaximum: false) ] static func specification(for processor: String, cpuCoreCount: Int = ProcessInfo.processInfo.activeProcessorCount) -> MacMemorySpecification? { let normalized = processor.replacingOccurrences(of: "Apple ", with: "") return specifications.first { normalized == $0.chip && ($0.cpuCoreCount == nil || $0.cpuCoreCount == cpuCoreCount) } } static func description(for processor: String, cpuCoreCount: Int = ProcessInfo.processInfo.activeProcessorCount) -> String { specification(for: processor, cpuCoreCount: cpuCoreCount)?.displayValue ?? "Clock not published by macOS" } } enum UpdateSpeed: String, CaseIterable, Identifiable { case high = "High" case normal = "Normal" case low = "Low" case paused = "Paused" var id: String { rawValue } var interval: Duration { switch self { case .high: .seconds(1) case .normal: .seconds(2) case .low: .seconds(5) case .paused: .seconds(2) } } } struct ProcessRecord: Identifiable, Hashable, Sendable { let pid: Int32 let parentPID: Int32 let name: String let executablePath: String let user: String let cpu: Double let memoryPercent: Double let residentBytes: UInt64 let state: String let elapsed: String let cpuTime: TimeInterval let threadCount: Int let openFileCount: Int let architecture: String let priority: Int let nice: Int var id: Int32 { pid } var displayName: String { let cleaned = name.trimmingCharacters(in: .whitespacesAndNewlines) return cleaned.isEmpty ? "Process \(pid)" : cleaned } } struct AppUsageRecord: Identifiable, Codable, Hashable, Sendable { let id: String var name: String var executablePath: String var user: String var cpuSeconds: TimeInterval var networkReceivedBytes: UInt64 var networkSentBytes: UInt64 var lastSeen: Date var networkTotalBytes: UInt64 { networkReceivedBytes + networkSentBytes } } struct ProcessActivityRate: Hashable, Sendable { var diskRead = 0.0 var diskWrite = 0.0 var networkReceive = 0.0 var networkSend = 0.0 var diskTotal: Double { diskRead + diskWrite } var networkTotal: Double { networkReceive + networkSend } } enum ServiceDomain: String, CaseIterable, Identifiable, Sendable { case user = "User" case system = "System" var id: String { rawValue } var launchctlPrefix: String { self == .system ? "system" : "gui/\(getuid())" } } struct ServiceRecord: Identifiable, Hashable, Sendable { let label: String let pid: Int32? let lastExitStatus: Int32 let domain: ServiceDomain var id: String { "\(domain.rawValue):\(label)" } var isRunning: Bool { pid != nil } var isControllable: Bool { domain == .user } var configurationPath: String? { let roots = domain == .system ? ["/Library/LaunchDaemons", "/System/Library/LaunchDaemons"] : [NSHomeDirectory() + "/Library/LaunchAgents", "/Library/LaunchAgents", "/System/Library/LaunchAgents"] return roots.map { "\($0)/\(label).plist" }.first { FileManager.default.fileExists(atPath: $0) } } var displayName: String { let parts = label.split(separator: ".").map(String.init) let meaningful = parts.filter { part in let isNumber = part.allSatisfy(\.isNumber) let isUUID = UUID(uuidString: part) != nil return !isNumber && !isUUID } guard let last = meaningful.last else { return label } let generic = ["agent", "helper", "service", "xpcservice", "app"] if generic.contains(last.lowercased()), meaningful.count > 1 { return "\(meaningful[meaningful.count - 2]) \(last)" } return last } var publisher: String { let parts = label.split(separator: ".").map(String.init) guard let first = parts.first else { return "Unknown" } if label.hasPrefix("com.apple") { return "Apple" } if first == "application", parts.count > 2 { let vendorIndex = ["com", "org", "net", "io"].contains(parts[1].lowercased()) ? 2 : 1 let vendor = parts[vendorIndex] return vendor.prefix(1).uppercased() + vendor.dropFirst() } if ["com", "org", "net", "io", "us"].contains(first.lowercased()), parts.count > 1 { return parts[1].prefix(1).uppercased() + parts[1].dropFirst() } return first.prefix(1).uppercased() + first.dropFirst() } } struct MetricSample: Identifiable { let id = UUID() let date: Date let value: Double } struct VolumeSnapshot: Identifiable, Hashable, Sendable { let id: String let name: String let mountPath: String let fileSystem: String let capacity: UInt64 let available: UInt64 let isEjectable: Bool var used: UInt64 { capacity > available ? capacity - available : 0 } var usedPercent: Double { capacity > 0 ? Double(used) / Double(capacity) * 100 : 0 } } struct SystemSnapshot { var cpuPercent = 0.0 var memoryUsed: UInt64 = 0 var memoryTotal: UInt64 = ProcessInfo.processInfo.physicalMemory var memoryActive: UInt64 = 0 var memoryWired: UInt64 = 0 var memoryCompressed: UInt64 = 0 var memoryCached: UInt64 = 0 var swapUsed: UInt64 = 0 var swapTotal: UInt64 = 0 var memoryType = "Unified" var memorySpeed = "Not reported by macOS" var memoryManufacturer = "Apple unified memory" var diskFree: UInt64 = 0 var diskTotal: UInt64 = 0 var diskReadRate = 0.0 var diskWriteRate = 0.0 var diskOperationsPerSecond = 0.0 var diskLatencyMilliseconds = 0.0 var diskActivePercent = 0.0 var gpuPercent = 0.0 var gpuRendererPercent = 0.0 var gpuTilerPercent = 0.0 var gpuMemoryBytes: UInt64 = 0 var gpuAllocatedBytes: UInt64 = 0 var gpuCoreCount = 0 var processCount = 0 var threadCount = 0 var uptime: TimeInterval = ProcessInfo.processInfo.systemUptime var corePercents: [Double] = [] var networkReceiveRate = 0.0 var networkSendRate = 0.0 var networkInterface = "Network" var removableVolumes: [VolumeSnapshot] = [] var memoryPercent: Double { guard memoryTotal > 0 else { return 0 } return Double(memoryUsed) / Double(memoryTotal) * 100 } var diskPercent: Double { guard diskTotal > 0 else { return 0 } return Double(diskTotal - diskFree) / Double(diskTotal) * 100 } var diskThroughput: Double { diskReadRate + diskWriteRate } } @MainActor enum Formatters { static let bytes: ByteCountFormatter = { let formatter = ByteCountFormatter() formatter.countStyle = .memory formatter.allowedUnits = [.useKB, .useMB, .useGB, .useTB] return formatter }() static func percent(_ value: Double) -> String { value < 10 ? String(format: "%.1f%%", value) : String(format: "%.0f%%", value) } static func uptime(_ interval: TimeInterval) -> String { let total = Int(interval) let days = total / 86_400 let hours = (total % 86_400) / 3_600 let minutes = (total % 3_600) / 60 return days > 0 ? "\(days)d \(hours)h \(minutes)m" : "\(hours)h \(minutes)m" } static func rate(_ bytesPerSecond: Double) -> String { "\(bytes.string(fromByteCount: Int64(max(0, bytesPerSecond))))/s" } static func duration(_ interval: TimeInterval) -> String { let total = max(0, Int(interval.rounded())) let hours = total / 3_600 let minutes = (total % 3_600) / 60 let seconds = total % 60 return hours > 0 ? String(format: "%d:%02d:%02d", hours, minutes, seconds) : String(format: "%02d:%02d", minutes, seconds) } }