// // DisksView.swift // MUA // // Created by Mitchel Volkering on 21/12/2025. // import SwiftUI /// Shows all connected disks with detailed info struct DisksView: View { @State private var disks: [DiskInfo] = [] @State private var selectedDisk: DiskInfo? var body: some View { ScrollView { LazyVStack(spacing: 16) { ForEach(disks) { disk in DiskCardView( disk: disk, isSelected: selectedDisk?.id == disk.id ) .onTapGesture { withAnimation(.spring(response: 0.3)) { selectedDisk = disk } } } } .padding() } .background(Color(nsColor: .windowBackgroundColor)) .onAppear { refreshDisks() } .toolbar { ToolbarItem { Button { refreshDisks() } label: { Label("Refresh", systemImage: "arrow.clockwise") } } } } private func refreshDisks() { disks = DiskInfo.getAllDisks() } } struct DiskCardView: View { let disk: DiskInfo let isSelected: Bool @State private var isHovered = false var body: some View { HStack(spacing: 20) { // Disk icon with glass effect ZStack { RoundedRectangle(cornerRadius: 16) .fill( .linearGradient( colors: [ iconColor.opacity(0.6), iconColor.opacity(0.3) ], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .frame(width: 72, height: 72) .overlay( RoundedRectangle(cornerRadius: 16) .fill( .linearGradient( colors: [.white.opacity(0.3), .clear], startPoint: .topLeading, endPoint: .center ) ) ) .overlay( RoundedRectangle(cornerRadius: 16) .strokeBorder(.white.opacity(0.3), lineWidth: 1) ) .shadow(color: iconColor.opacity(0.3), radius: isHovered ? 12 : 6) Image(systemName: disk.icon) .font(.system(size: 32)) .foregroundStyle(.white) } VStack(alignment: .leading, spacing: 8) { HStack { Text(disk.name) .font(.title3) .fontWeight(.semibold) if disk.isRemovable { Text("Removable") .font(.caption2) .padding(.horizontal, 6) .padding(.vertical, 2) .background(.quaternary, in: Capsule()) } } Text(disk.mountPoint.path) .font(.caption) .foregroundStyle(.tertiary) // Usage bar GeometryReader { geo in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 4) .fill(.quaternary) RoundedRectangle(cornerRadius: 4) .fill(usageGradient) .frame(width: geo.size.width * CGFloat(disk.usedPercentage / 100)) } } .frame(height: 10) HStack { Text("\(disk.formattedUsedSpace) used") .foregroundStyle(.secondary) Text("•") .foregroundStyle(.quaternary) Text("\(disk.formattedFreeSpace) free") .foregroundStyle(.secondary) Spacer() Text(disk.formattedTotalSpace) .foregroundStyle(.tertiary) } .font(.caption) } Spacer() // Usage percentage VStack { Text("\(Int(disk.usedPercentage))%") .font(.system(size: 24, weight: .semibold, design: .rounded)) .foregroundStyle(usageColor) Text("used") .font(.caption2) .foregroundStyle(.tertiary) } } .padding() .background( RoundedRectangle(cornerRadius: 16) .fill(.regularMaterial) .overlay( RoundedRectangle(cornerRadius: 16) .strokeBorder( isSelected ? Color.accentColor.opacity(0.5) : Color.clear, lineWidth: 2 ) ) ) .shadow(color: .black.opacity(0.05), radius: 8, y: 4) .scaleEffect(isHovered ? 1.01 : 1.0) .animation(.spring(response: 0.3), value: isHovered) .onHover { hovering in isHovered = hovering } } private var iconColor: Color { if disk.isRemovable { return .orange } if disk.isInternal { return .blue } return .purple } private var usageColor: Color { let percentage = disk.usedPercentage if percentage > 90 { return .red } if percentage > 75 { return .orange } return .green } private var usageGradient: LinearGradient { let color = usageColor return .linearGradient( colors: [color.opacity(0.8), color], startPoint: .leading, endPoint: .trailing ) } } #Preview { DisksView() .frame(width: 700, height: 500) }