import Charts import SwiftUI struct PerformanceView: View { @Environment(SystemMonitor.self) private var monitor @AppStorage("performanceSelectedMetric") private var selectedMetric: PerformanceMetric = .cpu @AppStorage("performanceCPUDisplayMode") private var cpuDisplayMode: CPUDisplayMode = .summary @State private var selectedVolumeID: String? private enum PerformanceMetric: String, CaseIterable, Identifiable { case cpu = "CPU" case memory = "Memory" case disk = "Disk" case gpu = "GPU" case network = "Network" var id: String { rawValue } var icon: String { switch self { case .cpu: "cpu" case .memory: "memorychip" case .disk: "internaldrive" case .gpu: "rectangle.3.group" case .network: "network" } } var color: Color { switch self { case .cpu: .blue case .memory: .purple case .disk: .green case .gpu: .pink case .network: .teal } } } var body: some View { VStack(spacing: 0) { PageHeader(title: "Performance") { UpdateStatus() } HStack(spacing: 0) { ScrollView { VStack(spacing: 10) { metricCard(.cpu, value: monitor.snapshot.cpuPercent, detail: "\(ProcessInfo.processInfo.activeProcessorCount) logical processors") metricCard(.memory, value: monitor.snapshot.memoryPercent, detail: "\(Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryUsed))) of \(Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryTotal)))") metricCard( .disk, value: monitor.snapshot.diskActivePercent, valueText: Formatters.rate(monitor.snapshot.diskThroughput), detail: "\(Formatters.percent(monitor.snapshot.diskActivePercent)) active • System volume" ) ForEach(monitor.snapshot.removableVolumes) { volume in volumeCard(volume) } metricCard( .gpu, value: monitor.snapshot.gpuPercent, detail: "\(monitor.snapshot.gpuCoreCount) cores • \(Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.gpuMemoryBytes))) in use" ) metricCard( .network, value: 0, valueText: Formatters.rate(monitor.snapshot.networkReceiveRate + monitor.snapshot.networkSendRate), detail: "↓ \(Formatters.rate(monitor.snapshot.networkReceiveRate)) ↑ \(Formatters.rate(monitor.snapshot.networkSendRate))" ) } .padding(14) } .frame(width: 245) .background(Color(nsColor: .controlBackgroundColor).opacity(0.45)) Divider() detail } } } private func metricCard(_ metric: PerformanceMetric, value: Double, valueText: String? = nil, detail: String) -> some View { Button { selectedVolumeID = nil selectedMetric = metric } label: { HStack(spacing: 12) { Image(systemName: metric.icon) .font(.title3) .foregroundStyle(metric.color) .frame(width: 30) VStack(alignment: .leading, spacing: 3) { Text(metric.rawValue).font(.headline) Text(valueText ?? Formatters.percent(value)).font(.title3.monospacedDigit()) Text(detail).font(.caption).foregroundStyle(.secondary).lineLimit(1) } Spacer() } .padding(12) .frame(maxWidth: .infinity, alignment: .leading) .background(selectedVolumeID == nil && selectedMetric == metric ? metric.color.opacity(0.13) : Color(nsColor: .windowBackgroundColor)) .overlay { RoundedRectangle(cornerRadius: 9) .stroke( selectedVolumeID == nil && selectedMetric == metric ? metric.color : Color.secondary.opacity(0.15), lineWidth: selectedVolumeID == nil && selectedMetric == metric ? 1.5 : 1 ) } .clipShape(RoundedRectangle(cornerRadius: 9)) } .buttonStyle(.plain) } private func volumeCard(_ volume: VolumeSnapshot) -> some View { Button { selectedVolumeID = volume.id } label: { HStack(spacing: 12) { Image(systemName: volume.isEjectable ? "externaldrive.badge.checkmark" : "externaldrive") .font(.title3) .foregroundStyle(.orange) .frame(width: 30) VStack(alignment: .leading, spacing: 3) { Text(volume.name).font(.headline).lineLimit(1) Text(Formatters.percent(volume.usedPercent)).font(.title3.monospacedDigit()) Text("\(Formatters.bytes.string(fromByteCount: Int64(volume.capacity))) • \(volume.fileSystem)") .font(.caption).foregroundStyle(.secondary).lineLimit(1) } Spacer() } .padding(12) .frame(maxWidth: .infinity, alignment: .leading) .background(selectedVolumeID == volume.id ? Color.orange.opacity(0.13) : Color(nsColor: .windowBackgroundColor)) .overlay { RoundedRectangle(cornerRadius: 9) .stroke(selectedVolumeID == volume.id ? Color.orange : Color.secondary.opacity(0.15), lineWidth: selectedVolumeID == volume.id ? 1.5 : 1) } .clipShape(RoundedRectangle(cornerRadius: 9)) } .buttonStyle(.plain) .accessibilityLabel("\(volume.name), removable disk") .accessibilityValue("\(Formatters.percent(volume.usedPercent)) used") } @ViewBuilder private var detail: some View { if let selectedVolumeID, let volume = monitor.snapshot.removableVolumes.first(where: { $0.id == selectedVolumeID }) { RemovableDiskDetail(volume: volume) } else { switch selectedMetric { case .cpu: CPUPerformanceDetail( subtitle: processorName, value: monitor.snapshot.cpuPercent, history: monitor.cpuHistory, coreHistories: monitor.coreHistories, coreValues: monitor.snapshot.corePercents, displayMode: $cpuDisplayMode, stats: [ ("Processes", "\(monitor.snapshot.processCount)"), ("Threads", "\(monitor.snapshot.threadCount)"), ("Uptime", Formatters.uptime(monitor.snapshot.uptime)), ("Logical processors", "\(ProcessInfo.processInfo.activeProcessorCount)") ] ) case .memory: PerformanceDetail( title: "Memory", subtitle: Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryTotal)), color: .purple, value: monitor.snapshot.memoryPercent, history: monitor.memoryHistory, stats: [ ("In use", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryUsed))), ("Available", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryTotal - monitor.snapshot.memoryUsed))), ("Active", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryActive))), ("Cached", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryCached))), ("Wired", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryWired))), ("Compressed", Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.memoryCompressed))), ("Swap", "\(Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.swapUsed))) / \(Formatters.bytes.string(fromByteCount: Int64(monitor.snapshot.swapTotal)))"), ("Type", monitor.snapshot.memoryType), ("Frequency / speed", monitor.snapshot.memorySpeed), ("Manufacturer", monitor.snapshot.memoryManufacturer) ] ) case .disk: DiskPerformanceDetail( snapshot: monitor.snapshot, readHistory: monitor.diskReadHistory, writeHistory: monitor.diskWriteHistory, latencyHistory: monitor.diskLatencyHistory, activeHistory: monitor.diskActiveHistory ) case .gpu: GPUPerformanceDetail( name: "\(processorName) GPU", snapshot: monitor.snapshot, history: monitor.gpuHistory, rendererHistory: monitor.gpuRendererHistory, tilerHistory: monitor.gpuTilerHistory ) case .network: NetworkPerformanceDetail( interface: monitor.snapshot.networkInterface, receiveRate: monitor.snapshot.networkReceiveRate, sendRate: monitor.snapshot.networkSendRate, receiveHistory: monitor.networkReceiveHistory, sendHistory: monitor.networkSendHistory ) } } } private var processorName: String { var size = 0 guard sysctlbyname("machdep.cpu.brand_string", nil, &size, nil, 0) == 0, size > 1 else { return "Apple Silicon" } var buffer = [CChar](repeating: 0, count: size) guard sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nil, 0) == 0 else { return "Apple Silicon" } return String(decoding: buffer.prefix { $0 != 0 }.map(UInt8.init(bitPattern:)), as: UTF8.self) } } private struct RemovableDiskDetail: View { let volume: VolumeSnapshot var body: some View { ScrollView { VStack(alignment: .leading, spacing: 18) { VStack(alignment: .leading, spacing: 4) { Text(volume.name).font(.system(size: 30, weight: .semibold)) Text("\(volume.fileSystem) • \(volume.mountPath)") .foregroundStyle(.secondary) } HStack { Label(volume.isEjectable ? "Ejectable" : "Removable", systemImage: "externaldrive") .foregroundStyle(.orange) Spacer() Text(Formatters.percent(volume.usedPercent)) .font(.system(size: 34, weight: .light).monospacedDigit()) } ProgressView(value: volume.usedPercent, total: 100) .tint(.orange) StatsGrid(stats: [ ("Used", Formatters.bytes.string(fromByteCount: Int64(volume.used))), ("Available", Formatters.bytes.string(fromByteCount: Int64(volume.available))), ("Capacity", Formatters.bytes.string(fromByteCount: Int64(volume.capacity))), ("Ejectable", volume.isEjectable ? "Yes" : "No") ]) } .padding(22) } } } private struct GPUPerformanceDetail: View { let name: String let snapshot: SystemSnapshot let history: [MetricSample] let rendererHistory: [MetricSample] let tilerHistory: [MetricSample] var body: some View { ScrollView { VStack(alignment: .leading, spacing: 18) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { Text("GPU").font(.system(size: 30, weight: .semibold)) Text(name).foregroundStyle(.secondary) } Spacer() Text(Formatters.percent(snapshot.gpuPercent)) .font(.system(size: 34, weight: .light).monospacedDigit()) } VStack(alignment: .leading, spacing: 8) { Text("Overall utilization").font(.caption).foregroundStyle(.secondary) Chart(history) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Utilization", sample.value)) .foregroundStyle(Color.pink.opacity(0.12)) LineMark(x: .value("Time", sample.date), y: .value("Utilization", sample.value)) .foregroundStyle(.pink).lineStyle(.init(lineWidth: 2)) } .chartYScale(domain: 0...100) .chartXAxis(.hidden) .chartYAxis { AxisMarks(position: .trailing, values: [0, 25, 50, 75, 100]) { value in AxisGridLine().foregroundStyle(Color.pink.opacity(0.16)) AxisValueLabel { if let number = value.as(Int.self) { Text("\(number)%") } } } } .frame(height: 240) Text("60 seconds").font(.caption).foregroundStyle(.secondary) } .padding(16) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay { RoundedRectangle(cornerRadius: 12).stroke(Color.pink.opacity(0.16)) } HStack(spacing: 14) { engineChart(title: "Renderer", value: snapshot.gpuRendererPercent, history: rendererHistory, color: .pink) engineChart(title: "Tiler", value: snapshot.gpuTilerPercent, history: tilerHistory, color: .purple) } StatsGrid(stats: [ ("Utilization", Formatters.percent(snapshot.gpuPercent)), ("Renderer", Formatters.percent(snapshot.gpuRendererPercent)), ("Tiler", Formatters.percent(snapshot.gpuTilerPercent)), ("GPU cores", "\(snapshot.gpuCoreCount)"), ("Memory in use", Formatters.bytes.string(fromByteCount: Int64(snapshot.gpuMemoryBytes))), ("Allocated", Formatters.bytes.string(fromByteCount: Int64(snapshot.gpuAllocatedBytes))) ]) } .padding(22) } } private func engineChart(title: String, value: Double, history: [MetricSample], color: Color) -> some View { VStack(alignment: .leading, spacing: 6) { HStack { Text(title).font(.caption).foregroundStyle(.secondary) Spacer() Text(Formatters.percent(value)).font(.callout.monospacedDigit()) } Chart(history) { sample in AreaMark(x: .value("Time", sample.date), y: .value(title, sample.value)) .foregroundStyle(color.opacity(0.1)) LineMark(x: .value("Time", sample.date), y: .value(title, sample.value)) .foregroundStyle(color) } .chartYScale(domain: 0...100) .chartXAxis(.hidden) .chartYAxis(.hidden) .frame(height: 92) } .padding(14) .frame(maxWidth: .infinity) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 10)) .overlay { RoundedRectangle(cornerRadius: 10).stroke(color.opacity(0.16)) } .accessibilityElement(children: .combine) .accessibilityLabel("\(title) GPU utilization") .accessibilityValue(Formatters.percent(value)) } } private struct DiskPerformanceDetail: View { let snapshot: SystemSnapshot let readHistory: [MetricSample] let writeHistory: [MetricSample] let latencyHistory: [MetricSample] let activeHistory: [MetricSample] private var maximumRate: Double { let maximum = (readHistory.map(\.value) + writeHistory.map(\.value) + [snapshot.diskReadRate, snapshot.diskWriteRate]).max() ?? 0 return max(1024, maximum * 1.15) } private var maximumLatency: Double { max(0.1, ((latencyHistory.map(\.value) + [snapshot.diskLatencyMilliseconds]).max() ?? 0) * 1.15) } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 18) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { Text("Disk 0").font(.system(size: 30, weight: .semibold)) Text("System volume • APFS • Solid-state").foregroundStyle(.secondary) } Spacer() VStack(alignment: .trailing, spacing: 2) { Text(Formatters.rate(snapshot.diskThroughput)) .font(.system(size: 30, weight: .light).monospacedDigit()) Text("\(Formatters.percent(snapshot.diskActivePercent)) active") .font(.caption).foregroundStyle(.secondary) } } VStack(alignment: .leading, spacing: 8) { HStack { Text("Disk transfer rate").font(.caption).foregroundStyle(.secondary) Spacer() Label("Read", systemImage: "arrow.down").foregroundStyle(.green) Label("Write", systemImage: "arrow.up").foregroundStyle(.orange) } .font(.caption) Chart { ForEach(readHistory) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Read", sample.value)) .foregroundStyle(Color.green.opacity(0.1)) LineMark(x: .value("Time", sample.date), y: .value("Read", sample.value)) .foregroundStyle(by: .value("Direction", "Read")) } ForEach(writeHistory) { sample in LineMark(x: .value("Time", sample.date), y: .value("Write", sample.value)) .foregroundStyle(by: .value("Direction", "Write")) } } .chartForegroundStyleScale(["Read": Color.green, "Write": Color.orange]) .chartYScale(domain: 0...maximumRate) .chartXAxis(.hidden) .chartYAxis { AxisMarks(position: .trailing) { value in AxisGridLine().foregroundStyle(Color.green.opacity(0.14)) AxisValueLabel { if let rate = value.as(Double.self) { Text(Formatters.rate(rate)) } } } } .chartLegend(.hidden) .frame(height: 220) Text("60 seconds").font(.caption).foregroundStyle(.secondary) } .padding(16) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay { RoundedRectangle(cornerRadius: 12).stroke(Color.green.opacity(0.16)) } HStack(spacing: 14) { miniChart( title: "Average response time", value: String(format: "%.2f ms", snapshot.diskLatencyMilliseconds), history: latencyHistory, maximum: maximumLatency, color: .orange ) miniChart( title: "Active time", value: Formatters.percent(snapshot.diskActivePercent), history: activeHistory, maximum: 100, color: .green ) } StatsGrid(stats: [ ("Read speed", Formatters.rate(snapshot.diskReadRate)), ("Write speed", Formatters.rate(snapshot.diskWriteRate)), ("Response time", String(format: "%.2f ms", snapshot.diskLatencyMilliseconds)), ("IOPS", String(format: "%.0f", snapshot.diskOperationsPerSecond)), ("Used space", Formatters.bytes.string(fromByteCount: Int64(snapshot.diskTotal - snapshot.diskFree))), ("Capacity", Formatters.bytes.string(fromByteCount: Int64(snapshot.diskTotal))) ]) } .padding(22) } } private func miniChart( title: String, value: String, history: [MetricSample], maximum: Double, color: Color ) -> some View { VStack(alignment: .leading, spacing: 6) { HStack { Text(title).font(.caption).foregroundStyle(.secondary) Spacer() Text(value).font(.callout.monospacedDigit()) } Chart(history) { sample in AreaMark(x: .value("Time", sample.date), y: .value(title, sample.value)) .foregroundStyle(color.opacity(0.1)) LineMark(x: .value("Time", sample.date), y: .value(title, sample.value)) .foregroundStyle(color) } .chartYScale(domain: 0...maximum) .chartXAxis(.hidden) .chartYAxis(.hidden) .frame(height: 92) .accessibilityLabel(title) .accessibilityValue(value) } .padding(14) .frame(maxWidth: .infinity) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 10)) .overlay { RoundedRectangle(cornerRadius: 10).stroke(color.opacity(0.16)) } } } private struct NetworkPerformanceDetail: View { let interface: String let receiveRate: Double let sendRate: Double let receiveHistory: [MetricSample] let sendHistory: [MetricSample] private var maximumRate: Double { let maximum = (receiveHistory.map(\.value) + sendHistory.map(\.value) + [receiveRate, sendRate]).max() ?? 0 return max(1024, maximum * 1.15) } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 20) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { Text("Network").font(.system(size: 30, weight: .semibold)) Text(interface).foregroundStyle(.secondary) } Spacer() Text(Formatters.rate(receiveRate + sendRate)) .font(.system(size: 30, weight: .light).monospacedDigit()) } VStack(alignment: .leading, spacing: 8) { HStack { Text("Throughput").font(.caption).foregroundStyle(.secondary) Spacer() Label("Receive", systemImage: "arrow.down").foregroundStyle(.teal) Label("Send", systemImage: "arrow.up").foregroundStyle(.purple) } .font(.caption) Chart { ForEach(receiveHistory) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Receive", sample.value)) .foregroundStyle(Color.teal.opacity(0.1)) LineMark(x: .value("Time", sample.date), y: .value("Receive", sample.value)) .foregroundStyle(by: .value("Direction", "Receive")) } ForEach(sendHistory) { sample in LineMark(x: .value("Time", sample.date), y: .value("Send", sample.value)) .foregroundStyle(by: .value("Direction", "Send")) } } .chartForegroundStyleScale(["Receive": Color.teal, "Send": Color.purple]) .chartYScale(domain: 0...maximumRate) .chartXAxis(.hidden) .chartYAxis { AxisMarks(position: .trailing) { value in AxisGridLine().foregroundStyle(Color.teal.opacity(0.14)) AxisValueLabel { if let rate = value.as(Double.self) { Text(Formatters.rate(rate)) } } } } .chartLegend(.hidden) .frame(height: 280) Text("60 seconds").font(.caption).foregroundStyle(.secondary) } .padding(16) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay { RoundedRectangle(cornerRadius: 12).stroke(Color.teal.opacity(0.16)) } StatsGrid(stats: [ ("Receive", Formatters.rate(receiveRate)), ("Send", Formatters.rate(sendRate)), ("Combined", Formatters.rate(receiveRate + sendRate)), ("Interface", interface) ]) } .padding(22) } } } private enum CPUDisplayMode: String { case summary = "Summary view" case logicalProcessors = "Logical processors" } private struct CPUPerformanceDetail: View { let subtitle: String let value: Double let history: [MetricSample] let coreHistories: [[MetricSample]] let coreValues: [Double] @Binding var displayMode: CPUDisplayMode let stats: [(String, String)] private var chartData: [MetricSample] { if !history.isEmpty { return history } return [MetricSample(date: .now.addingTimeInterval(-60), value: value), MetricSample(date: .now, value: value)] } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { Text("CPU").font(.system(size: 30, weight: .semibold)) Text(subtitle).foregroundStyle(.secondary) } Spacer() Text(Formatters.percent(value)) .font(.system(size: 34, weight: .light).monospacedDigit()) } cpuGraph .contextMenu { Button { displayMode = .summary } label: { Label("Summary view", systemImage: displayMode == .summary ? "checkmark" : "chart.xyaxis.line") } Button { displayMode = .logicalProcessors } label: { Label("Logical processors", systemImage: displayMode == .logicalProcessors ? "checkmark" : "square.grid.3x3") } } .help("Right-click to switch between summary and logical processor graphs") StatsGrid(stats: stats) } .padding(22) } } private var cpuGraph: some View { VStack(alignment: .leading, spacing: 8) { HStack { Text(displayMode == .summary ? "% Utilization" : "Utilization by logical processor") .font(.caption) .foregroundStyle(.secondary) Spacer() Label(displayMode.rawValue, systemImage: displayMode == .summary ? "chart.xyaxis.line" : "square.grid.3x3") .font(.caption) .foregroundStyle(.secondary) } if displayMode == .summary { Chart(chartData) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(Color.blue.opacity(0.13)) LineMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(.blue) .lineStyle(.init(lineWidth: 2)) } .chartYScale(domain: 0...100) .chartXAxis(.hidden) .chartYAxis { AxisMarks(position: .trailing, values: [0, 25, 50, 75, 100]) { value in AxisGridLine().foregroundStyle(Color.blue.opacity(0.18)) AxisValueLabel { if let number = value.as(Int.self) { Text("\(number)%") } } } } .frame(height: 280) } else { LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 4), spacing: 8) { ForEach(coreValues.indices, id: \.self) { index in CoreChart( index: index, value: coreValues[index], history: index < coreHistories.count ? coreHistories[index] : [] ) } } .frame(minHeight: 280) } Text("60 seconds").font(.caption).foregroundStyle(.secondary) } .padding(16) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay { RoundedRectangle(cornerRadius: 12) .stroke(Color.blue.opacity(0.16), lineWidth: 1) } } } private struct CoreChart: View { let index: Int let value: Double let history: [MetricSample] private var data: [MetricSample] { if !history.isEmpty { return history } return [MetricSample(date: .now.addingTimeInterval(-60), value: value), MetricSample(date: .now, value: value)] } var body: some View { ZStack(alignment: .topLeading) { Chart(data) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(Color.blue.opacity(0.1)) LineMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(.blue) .lineStyle(.init(lineWidth: 1.25)) } .chartYScale(domain: 0...100) .chartXAxis(.hidden) .chartYAxis(.hidden) .background { GridPattern().stroke(Color.blue.opacity(0.12), lineWidth: 0.5) } VStack(alignment: .leading, spacing: 1) { Text("CPU \(index)").font(.caption2.weight(.medium)) Text(Formatters.percent(value)).font(.caption2.monospacedDigit()).foregroundStyle(.secondary) } .padding(6) } .frame(height: 62) .clipShape(RoundedRectangle(cornerRadius: 5)) .overlay { RoundedRectangle(cornerRadius: 5).stroke(Color.blue.opacity(0.28), lineWidth: 1) } .accessibilityElement(children: .ignore) .accessibilityLabel("CPU \(index), \(Formatters.percent(value)) utilization") } } private struct GridPattern: Shape { func path(in rect: CGRect) -> Path { var path = Path() for fraction in [0.25, 0.5, 0.75] { let x = rect.width * fraction let y = rect.height * fraction path.move(to: CGPoint(x: x, y: 0)) path.addLine(to: CGPoint(x: x, y: rect.height)) path.move(to: CGPoint(x: 0, y: y)) path.addLine(to: CGPoint(x: rect.width, y: y)) } return path } } private struct StatsGrid: View { let stats: [(String, String)] var body: some View { LazyVGrid(columns: [.init(.flexible()), .init(.flexible())], spacing: 18) { ForEach(Array(stats.enumerated()), id: \.offset) { _, stat in VStack(alignment: .leading, spacing: 4) { Text(stat.0).font(.caption).foregroundStyle(.secondary) Text(stat.1).font(.title3.monospacedDigit()).lineLimit(1) } .frame(maxWidth: .infinity, alignment: .leading) } } } } private struct PerformanceDetail: View { let title: String let subtitle: String let color: Color let value: Double let history: [MetricSample] let stats: [(String, String)] var chartData: [MetricSample] { if !history.isEmpty { return history } return [MetricSample(date: .now.addingTimeInterval(-60), value: value), MetricSample(date: .now, value: value)] } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 20) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { Text(title).font(.system(size: 30, weight: .semibold)) Text(subtitle).foregroundStyle(.secondary) } Spacer() Text(Formatters.percent(value)) .font(.system(size: 34, weight: .light).monospacedDigit()) } VStack(alignment: .leading, spacing: 8) { Text("% Utilization").font(.caption).foregroundStyle(.secondary) Chart(chartData) { sample in AreaMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(color.opacity(0.13)) LineMark(x: .value("Time", sample.date), y: .value("Usage", sample.value)) .foregroundStyle(color) .lineStyle(.init(lineWidth: 2)) } .chartYScale(domain: 0...100) .chartXAxis(.hidden) .chartYAxis { AxisMarks(position: .trailing, values: [0, 25, 50, 75, 100]) { value in AxisGridLine().foregroundStyle(color.opacity(0.18)) AxisValueLabel { if let number = value.as(Int.self) { Text("\(number)%") } } } } .frame(minHeight: 280) Text("60 seconds").font(.caption).foregroundStyle(.secondary) } .padding(16) .background(Color(nsColor: .controlBackgroundColor).opacity(0.42)) .clipShape(RoundedRectangle(cornerRadius: 12)) StatsGrid(stats: stats) } .padding(22) } } }