// // DetailPanelView.swift // MUA // // Created by Mitchel Volkering on 21/12/2025. // import SwiftUI /// Shows detailed info about the selected item struct DetailPanelView: View { let item: FileItem? var body: some View { Group { if let item = item { ScrollView { VStack(alignment: .leading, spacing: 20) { // Header with icon and name headerSection(item) Divider() // Size information sizeSection(item) if item.isDirectory && !item.children.isEmpty { Divider() childrenSection(item) } Divider() // Path info pathSection(item) Spacer() } .padding() } } else { emptyState } } .frame(minWidth: 260, idealWidth: 280, maxWidth: 320) .background(.regularMaterial) } private func headerSection(_ item: FileItem) -> some View { HStack(spacing: 16) { // Large icon with glass effect ZStack { Circle() .fill( .linearGradient( colors: [ item.color.opacity(0.6), item.color.opacity(0.3) ], startPoint: .topLeading, endPoint: .bottomTrailing ) ) .frame(width: 64, height: 64) .overlay( Circle() .fill( .linearGradient( colors: [.white.opacity(0.3), .clear], startPoint: .topLeading, endPoint: .center ) ) ) .overlay( Circle() .strokeBorder(.white.opacity(0.3), lineWidth: 1) ) .shadow(color: item.color.opacity(0.3), radius: 8) Image(systemName: item.icon) .font(.system(size: 28)) .foregroundStyle(.white) } VStack(alignment: .leading, spacing: 4) { Text(item.name) .font(.headline) .lineLimit(2) Text(item.isDirectory ? "Folder" : item.url.pathExtension.uppercased()) .font(.subheadline) .foregroundStyle(.secondary) } } } private func sizeSection(_ item: FileItem) -> some View { VStack(alignment: .leading, spacing: 12) { Text("Size") .font(.subheadline) .foregroundStyle(.secondary) HStack(alignment: .firstTextBaseline) { Text(item.formattedSize) .font(.system(size: 28, weight: .semibold, design: .rounded)) if item.parent != nil { Text("(\(String(format: "%.1f", item.percentageOfParent()))% of parent)") .font(.caption) .foregroundStyle(.secondary) } } if item.isDirectory { HStack { Label("\(item.children.count) items", systemImage: "doc.on.doc") if item.descendantCount > item.children.count { Text("(\(item.descendantCount) total)") .foregroundStyle(.tertiary) } } .font(.caption) .foregroundStyle(.secondary) } } } private func childrenSection(_ item: FileItem) -> some View { VStack(alignment: .leading, spacing: 12) { Text("Largest Items") .font(.subheadline) .foregroundStyle(.secondary) ForEach(item.topChildren(count: 5)) { child in HStack { Image(systemName: child.icon) .foregroundStyle(child.color) .frame(width: 20) Text(child.name) .lineLimit(1) .truncationMode(.middle) Spacer() Text(child.formattedSize) .font(.caption) .foregroundStyle(.secondary) } .font(.caption) } } } private func pathSection(_ item: FileItem) -> some View { VStack(alignment: .leading, spacing: 8) { Text("Location") .font(.subheadline) .foregroundStyle(.secondary) Text(item.url.path) .font(.caption) .foregroundStyle(.tertiary) .lineLimit(3) .textSelection(.enabled) Button("Show in Finder") { NSWorkspace.shared.selectFile(item.url.path, inFileViewerRootedAtPath: item.url.deletingLastPathComponent().path) } .buttonStyle(.link) .font(.caption) } } private var emptyState: some View { VStack(spacing: 12) { Image(systemName: "square.dashed") .font(.system(size: 40)) .foregroundStyle(.quaternary) Text("Select an item") .font(.subheadline) .foregroundStyle(.secondary) Text("Click a bubble to see details") .font(.caption) .foregroundStyle(.tertiary) } .frame(maxWidth: .infinity, maxHeight: .infinity) } } #Preview("With Item") { let parent = FileItem(url: URL(fileURLWithPath: "/Users"), isDirectory: true, size: 100_000_000_000) let item = FileItem( url: URL(fileURLWithPath: "/Users/mitchel"), isDirectory: true, size: 45_000_000_000, children: [ FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Documents"), isDirectory: true, size: 15_000_000_000), FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Downloads"), isDirectory: true, size: 12_000_000_000), FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Library"), isDirectory: true, size: 8_000_000_000), FileItem(url: URL(fileURLWithPath: "/Users/mitchel/movie.mp4"), isDirectory: false, size: 5_000_000_000), ], parent: parent ) DetailPanelView(item: item) .frame(height: 500) } #Preview("Empty") { DetailPanelView(item: nil) .frame(height: 400) }