import AppKit import SwiftUI struct ContentView: View { @Environment(SystemMonitor.self) private var monitor @State private var selection: TaskSection? @State private var searchText = "" @State private var showingAbout = false @State private var showingNewTask = false @State private var detailSelection: Int32? @State private var columnVisibility: NavigationSplitViewVisibility = .all @AppStorage("sidebarCompact") private var sidebarCompact = false init() { let stored = UserDefaults.standard.string(forKey: "defaultStartPage") ?? TaskSection.processes.rawValue _selection = State(initialValue: TaskSection(rawValue: stored) ?? .processes) } var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { Sidebar(selection: $selection, isCompact: sidebarCompact) .navigationSplitViewColumnWidth( min: sidebarCompact ? 68 : 190, ideal: sidebarCompact ? 68 : 210, max: sidebarCompact ? 68 : 250 ) } detail: { Group { switch selection ?? .processes { case .processes: ProcessesView(searchText: searchText) { process in detailSelection = process.pid selection = .details } case .performance: PerformanceView() case .history: AppHistoryView(searchText: searchText) { process in detailSelection = process.pid selection = .details } case .startup: StartupAppsView() case .users: UsersView { process in detailSelection = process.pid selection = .details } case .details: DetailsView(searchText: searchText, selection: $detailSelection) case .services: ServicesView(searchText: searchText) { process in detailSelection = process.pid selection = .details } case .settings: SettingsView() } } .searchable(text: $searchText, placement: .toolbar, prompt: "Type a name, PID, or user") .toolbar { TaskToolbarContent(showingNewTask: $showingNewTask, showingAbout: $showingAbout) } } .navigationTitle("Task Manager") .animation(.snappy(duration: 0.22), value: sidebarCompact) .onChange(of: columnVisibility) { _, visibility in guard visibility == .detailOnly else { return } columnVisibility = .all DispatchQueue.main.asyncAfter(deadline: .now() + 0.08) { withAnimation(.snappy(duration: 0.22)) { sidebarCompact.toggle() } } } .sheet(isPresented: $showingNewTask) { NewTaskView(isPresented: $showingNewTask) } .alert("Task Manager", isPresented: Binding( get: { monitor.errorMessage != nil }, set: { if !$0 { monitor.errorMessage = nil } } )) { Button("OK", role: .cancel) { monitor.errorMessage = nil } } message: { Text(monitor.errorMessage ?? "") } .alert("Task Manager for macOS", isPresented: $showingAbout) { Button("OK", role: .cancel) { } } message: { Text("A native SwiftUI system monitor inspired by Windows Task Manager.") } .onAppear { DispatchQueue.main.async { NSApp.keyWindow?.level = UserDefaults.standard.bool(forKey: "alwaysOnTop") ? .floating : .normal } } } } private struct TaskToolbarContent: ToolbarContent { @Environment(SystemMonitor.self) private var monitor @Binding var showingNewTask: Bool @Binding var showingAbout: Bool var body: some ToolbarContent { @Bindable var monitor = monitor ToolbarItemGroup(placement: .primaryAction) { Button { showingNewTask = true } label: { Label("Run new task", systemImage: "plus.square") } .help("Launch an application or executable") Button { monitor.refresh() } label: { Label("Refresh", systemImage: "arrow.clockwise") } .help("Refresh now (⌘R)") Menu { UpdateSpeedPicker(selection: $monitor.updateSpeed) Divider() Button("About Task Manager") { showingAbout = true } } label: { Image(systemName: "ellipsis") } .menuStyle(.borderlessButton) } } } private struct Sidebar: View { @Binding var selection: TaskSection? let isCompact: Bool var body: some View { VStack(spacing: 0) { ScrollView { VStack(spacing: 4) { ForEach(TaskSection.allCases.filter { $0 != .settings }) { section in Button { selection = section } label: { sidebarLabel(section.rawValue, icon: section.icon) .padding(.horizontal, isCompact ? 0 : 8) .frame(height: 40) .contentShape(Rectangle()) } .buttonStyle(.plain) .frame(maxWidth: .infinity) .background(selection == section ? Color.accentColor.opacity(0.18) : .clear) .clipShape(RoundedRectangle(cornerRadius: 7)) .help(section.rawValue) .accessibilityLabel(section.rawValue) .accessibilityAddTraits(selection == section ? .isSelected : []) } } .padding(.horizontal, isCompact ? 8 : 10) .padding(.vertical, 8) } Divider().padding(.horizontal, isCompact ? 12 : 16) Button { selection = .settings } label: { sidebarLabel("Settings", icon: "gearshape") .padding(.horizontal, isCompact ? 0 : 8) .frame(height: 40) .contentShape(Rectangle()) } .buttonStyle(.plain) .font(.callout) .frame(maxWidth: .infinity) .background(selection == .settings ? Color.accentColor.opacity(0.18) : .clear) .clipShape(RoundedRectangle(cornerRadius: 7)) .padding(.horizontal, isCompact ? 8 : 10) .padding(.vertical, 8) .foregroundStyle(selection == .settings ? Color.primary : Color.secondary) .help("Settings") .accessibilityLabel("Settings") } } private func sidebarLabel(_ title: String, icon: String) -> some View { HStack(spacing: 10) { Image(systemName: icon) .frame(width: 22, height: 20) if !isCompact { Text(title).lineLimit(1) Spacer(minLength: 0) } } .frame(maxWidth: .infinity, alignment: isCompact ? .center : .leading) } }