import AppKit import SwiftUI extension Notification.Name { static let runNewTask = Notification.Name("Puter.runNewTask") } struct ContentView: View { @Environment(SystemMonitor.self) private var monitor @State private var selection: TaskSection? @State private var searchText = "" @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 } .searchable(text: $searchText, placement: .toolbar, prompt: "Search processes by name, PID, or user") case .performance: PerformanceView() case .history: AppHistoryView(searchText: searchText) { process in detailSelection = process.pid selection = .details } .searchable(text: $searchText, placement: .toolbar, prompt: "Search app history") case .startup: StartupAppsView() case .users: UsersView { process in detailSelection = process.pid selection = .details } case .details: DetailsView(searchText: searchText, selection: $detailSelection) .searchable(text: $searchText, placement: .toolbar, prompt: "Search details by name, PID, or user") case .services: ServicesView(searchText: searchText) { process in detailSelection = process.pid selection = .details } .searchable(text: $searchText, placement: .toolbar, prompt: "Search services by name, PID, or identifier") case .hardware: HardwareView() case .settings: SettingsView() } } .toolbar { TaskToolbarContent(showingNewTask: $showingNewTask) } } .navigationTitle("Puter") .animation(.snappy(duration: 0.22), value: sidebarCompact) .onChange(of: selection) { _, _ in searchText = "" } .onReceive(NotificationCenter.default.publisher(for: .runNewTask)) { _ in showingNewTask = true } .sheet(isPresented: $showingNewTask) { NewTaskView(isPresented: $showingNewTask) } .alert("Puter", isPresented: Binding( get: { monitor.errorMessage != nil }, set: { if !$0 { monitor.errorMessage = nil } } )) { Button("OK", role: .cancel) { monitor.errorMessage = nil } } message: { Text(monitor.errorMessage ?? "") } .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 @AppStorage("sidebarCompact") private var sidebarCompact = false 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() Toggle("Compact Sidebar", isOn: $sidebarCompact) } 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(minHeight: 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(minHeight: 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) } }