From a1a414ce838bef44d2d06d85947bc0a0a55e7a47 Mon Sep 17 00:00:00 2001 From: Chad Nelson Date: Sun, 9 Mar 2025 07:27:37 -0600 Subject: [PATCH] updated a lot of stuff --- MSG Desktop/ContentView.swift | 80 +++++++++++++++++++-------- MSG Desktop/MSG_DesktopApp.swift | 94 +++++++++++++++++++------------- 2 files changed, 113 insertions(+), 61 deletions(-) diff --git a/MSG Desktop/ContentView.swift b/MSG Desktop/ContentView.swift index 0d5af2f..0f145da 100644 --- a/MSG Desktop/ContentView.swift +++ b/MSG Desktop/ContentView.swift @@ -8,18 +8,29 @@ import SwiftUI struct ContentView: View { - @State public var output: String = "Please select an option \n" + @ObservedObject var appState = AppState.shared private let maxLines = 300 - @State private var showCancelButton: Bool = false + @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) +// 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: $output) + TextEditor(text: $appState.output) .font(.system(.body, design: .monospaced)) .frame(minWidth: 750, minHeight: 300) .padding() @@ -29,7 +40,7 @@ struct ContentView: View { .id("bottom") } } - .onChange(of: output) { _, _ in + .onChange(of: appState.output) { _, _ in trimLinesIfNeeded() withAnimation { proxy.scrollTo("bottom", anchor: .bottom) @@ -41,41 +52,64 @@ struct ContentView: View { DispatchQueue.main.async { self.showCancelButton = true } - runShellCommandAsync(command: "msg machine init", outputText: $output, showButton: $showCancelButton) + runShellCommandAsync(command: "msg machine init", outputText: $appState.output, showButton: $showCancelButton) }) { Text("Init") } + + Button(action: { - DispatchQueue.main.async { - self.showCancelButton = true - } - runShellCommandAsync(command: "rm -rf ~/.guix && msg machine init", outputText: $output, showButton: nil) }) { + 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: $output, showButton: nil) }) { + runShellCommandAsync(command: "msg machine start", outputText: $appState.output, showButton: nil) }) { Text("Start") } if showCancelButton { Button(action: { - runShellCommandAsync(command: "kill $(ps aux | grep '[g]uile' | awk '{print $2}')", outputText: $output, showButton: nil) - runShellCommandAsync(command: "kill $(ps aux | grep '[q]emu' | awk '{print $2}')", outputText: $output, showButton: nil) - DispatchQueue.main.async { - self.showCancelButton = false - }}) { + 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: $output, showButton: nil) }) { + 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: $output, showButton: nil) + 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") @@ -90,12 +124,12 @@ struct ContentView: View { private func trimLinesIfNeeded() { - let lines = output.split(separator: "\n") + let lines = AppState.shared.output.split(separator: "\n") if lines.count > maxLines { _ = lines.count - maxLines - let trimmedLines = lines.suffix(maxLines) // Keep only the last 300 lines - output = trimmedLines.joined(separator: "\n") // Join back to a single string + let trimmedLines = lines.suffix(maxLines) + AppState.shared.output = trimmedLines.joined(separator: "\n") } } } diff --git a/MSG Desktop/MSG_DesktopApp.swift b/MSG Desktop/MSG_DesktopApp.swift index 9fe5e55..2b28241 100644 --- a/MSG Desktop/MSG_DesktopApp.swift +++ b/MSG Desktop/MSG_DesktopApp.swift @@ -6,25 +6,10 @@ // import SwiftUI +import AppKit import Foundation -func oldshell(_ command: String) -> String { - let task = Process() - let pipe = Pipe() - - task.standardOutput = pipe - task.standardError = pipe - task.arguments = ["-c", command] - task.launchPath = "/bin/zsh" - task.standardInput = nil - task.launch() - - let data = pipe.fileHandleForReading.readDataToEndOfFile() - let output = String(data: data, encoding: .utf8)! - return output -} - -func runShellCommandAsync(command: String, outputText: Binding, showButton: Binding?) { +func runShellCommandAsync(command: String, outputText: Binding?, showButton: Binding?) { DispatchQueue.global(qos: .background).async { let task = Process() let pipe = Pipe() @@ -53,7 +38,9 @@ func runShellCommandAsync(command: String, outputText: Binding, showButt let data = fileHandle.availableData if let output = String(data: data, encoding: .utf8), !output.isEmpty { DispatchQueue.main.async { - outputText.wrappedValue += output + if let outputText = outputText { + outputText.wrappedValue += output + } } } fileHandle.waitForDataInBackgroundAndNotify() @@ -71,34 +58,65 @@ func runShellCommandAsync(command: String, outputText: Binding, showButt } } -func shell(_ command: String) -> String { - let task = Process() - let pipe = Pipe() - - task.standardOutput = pipe - task.standardError = pipe - task.arguments = ["-c", command] - task.launchPath = "/bin/zsh" - task.standardInput = nil +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationWillTerminate(_ notification: Notification) { + runShellCommandAsync(command: "msg machine stop", outputText: nil, showButton: nil) + runShellCommandAsync(command: "kill $(ps aux | grep '[g]uile' | awk '{print $2}')", outputText: nil, showButton: nil) + runShellCommandAsync(command: "kill $(ps aux | grep '[q]emu' | awk '{print $2}')", outputText: nil, showButton: nil) + } - task.environment = [ - "GUILE_LOAD_PATH": "/opt/homebrew/share/guile/site/3.0:/usr/local/share/guile/site/3.0/", - "GUILE_LOAD_COMPILED_PATH": "/opt/homebrew/lib/guile/3.0/site-ccache:/usr/local/lib/guile/3.0/site-ccache/", - "GUILE_SYSTEM_EXTENSIONS_PATH": "/opt/homebrew/lib/guile/3.0/extensions:/usr/local/lib/guile/3.0/extensions/", - "PATH": "$PATH:/opt/homebrew/bin:/usr/bin:/usr/local/bin:/bin" - ] + func saveAppState() { + print("App is terminating. Saving state...") + UserDefaults.standard.set(Date(), forKey: "lastClosed") + } + + var statusBarItem: NSStatusItem! + + func applicationDidFinishLaunching(_ notification: Notification) { + statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - task.launch() + if let button = statusBarItem.button { + button.title = "MSG" + button.toolTip = "MSG App" + } + + let menu = NSMenu() + + menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApp), keyEquivalent: "q")) + + statusBarItem.menu = menu + } + + @objc func quitApp() { + let alert = NSAlert() + alert.messageText = "Are you sure you want to quit?" + alert.informativeText = "Do you want to quit the app and exit?" + alert.alertStyle = .warning + + alert.addButton(withTitle: "Quit") + alert.addButton(withTitle: "Cancel") + + let response = alert.runModal() + + if response == .alertFirstButtonReturn { + NSApplication.shared.terminate(nil) + } + } - let data = pipe.fileHandleForReading.readDataToEndOfFile() - let output = String(data: data, encoding: .utf8) ?? "Error reading output" - - return output.trimmingCharacters(in: .whitespacesAndNewlines) } + +class AppState: ObservableObject { + @Published var output: String = "Please select an option \n" + + static let shared = AppState() +} + @main struct MSG_DesktopApp: App { + @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + var body: some Scene { WindowGroup { ContentView()