// // ContentView.swift // MSG Desktop // // Created by Chad Nelson on 3/8/25. // import SwiftUI struct ContentView: View { @ObservedObject var appState = AppState.shared private let maxLines = 300 @State private var showCancelButton: Bool = false @State private var showReinitPopup = false @State private var showCancelPopup = false var body: some View { VStack { // Image(systemName: "globe") // .imageScale(.large) // .foregroundStyle(.tint) let appIcon = NSImage(named: NSImage.applicationIconName) // Use SwiftUI's Image to display the icon Image(nsImage: appIcon ?? NSImage()) .resizable() // Optional: if you want to resize the icon .scaledToFit() // Optional: makes sure the aspect ratio is maintained ScrollViewReader { proxy in ScrollView { VStack(alignment: .leading) { TextEditor(text: $appState.output) .font(.system(.body, design: .monospaced)) .frame(minWidth: 750, minHeight: 300) .padding() .disabled(true) Text("") .id("bottom") } } .onChange(of: appState.output) { _, _ in trimLinesIfNeeded() withAnimation { proxy.scrollTo("bottom", anchor: .bottom) } } } HStack { Button(action: { DispatchQueue.main.async { self.showCancelButton = true } runShellCommandAsync(command: "msg machine init", outputText: $appState.output, showButton: $showCancelButton) }) { Text("Init") } Button(action: { showReinitPopup.toggle() }) { Text("Reinit") }.confirmationDialog("Are you sure?", isPresented: $showReinitPopup, titleVisibility: .visible) { Button("Confirm", role: .destructive) { DispatchQueue.main.async { self.showCancelButton = true runShellCommandAsync(command: "rm -rf ~/.guix && msg machine init", outputText: $appState.output, showButton: $showCancelButton) } } Button("Cancel", role: .cancel) {} } Button(action: { runShellCommandAsync(command: "msg machine start", outputText: $appState.output, showButton: nil) }) { Text("Start") } if showCancelButton { Button(action: { showCancelPopup.toggle() }) { Text("Cancel") }.confirmationDialog("Are you sure?", isPresented: $showCancelPopup, titleVisibility: .visible) { Button("Confirm", role: .destructive) { runShellCommandAsync(command: "kill $(ps aux | grep '[g]uile' | awk '{print $2}')", outputText: $appState.output, showButton: nil) runShellCommandAsync(command: "kill $(ps aux | grep '[q]emu' | awk '{print $2}')", outputText: $appState.output, showButton: nil) DispatchQueue.main.async { self.showCancelButton = false } } Button("Cancel", role: .cancel) {} } } if !showCancelButton{ Button(action: { runShellCommandAsync(command: "msg machine stop", outputText: $appState.output, showButton: nil) }) { Text("Stop") } } Button(action: { runShellCommandAsync(command: "osascript -e 'tell application \"Terminal\" to do script \"msg shell\"' -e 'tell application \"Terminal\" to activate'", outputText: $appState.output, showButton: nil) }) { Text("Shell") } } } .padding() .frame(width: 800, height: 400) } private func trimLinesIfNeeded() { let lines = AppState.shared.output.split(separator: "\n") if lines.count > maxLines { _ = lines.count - maxLines let trimmedLines = lines.suffix(maxLines) AppState.shared.output = trimmedLines.joined(separator: "\n") } } } #Preview { ContentView() }