diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/Contents.json b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/Contents.json index 3f00db4..366e387 100644 --- a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,51 +1,61 @@ { "images" : [ { + "filename" : "gigakamiguru -v.1.2 CMYK16x16.eps.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { + "filename" : "gigakamiguru -v.1.2 CMYK32x32.eps.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { + "filename" : "gigakamiguru -v.1.2 CMYK32x32.eps 1.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { + "filename" : "gigakamiguru -v.1.2 CMYK64x64.eps.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { + "filename" : "gigakamiguru -v.1.2 CMY128x128.eps.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { + "filename" : "gigakamiguru -v.1.2 CMY256x256.eps.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { + "filename" : "gigakamiguru -v.1.2 CMY256x256.eps 1.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { + "filename" : "gigakamiguru -v.1.2 CMY512x512.eps.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { + "filename" : "gigakamiguru -v.1.2 CMY512x512.eps 1.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { + "filename" : "gigakamiguru -v.1.2 CMY1024x1024.eps.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY1024x1024.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY1024x1024.eps.png new file mode 100644 index 0000000..ddcf78c Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY1024x1024.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY128x128.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY128x128.eps.png new file mode 100644 index 0000000..0486b0c Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY128x128.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps 1.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps 1.png new file mode 100644 index 0000000..2c5ee3c Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps 1.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps.png new file mode 100644 index 0000000..2c5ee3c Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY256x256.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps 1.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps 1.png new file mode 100644 index 0000000..465493e Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps 1.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps.png new file mode 100644 index 0000000..465493e Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMY512x512.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK16x16.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK16x16.eps.png new file mode 100644 index 0000000..76aafd1 Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK16x16.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps 1.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps 1.png new file mode 100644 index 0000000..d8d39d0 Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps 1.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps.png new file mode 100644 index 0000000..d8d39d0 Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK32x32.eps.png differ diff --git a/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK64x64.eps.png b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK64x64.eps.png new file mode 100644 index 0000000..4df0673 Binary files /dev/null and b/MSG Desktop/Assets.xcassets/AppIcon.appiconset/gigakamiguru -v.1.2 CMYK64x64.eps.png differ diff --git a/MSG Desktop/ContentView.swift b/MSG Desktop/ContentView.swift index 433b0f0..0d5af2f 100644 --- a/MSG Desktop/ContentView.swift +++ b/MSG Desktop/ContentView.swift @@ -8,15 +8,96 @@ import SwiftUI struct ContentView: View { + @State public var output: String = "Please select an option \n" + private let maxLines = 300 + @State private var showCancelButton: Bool = false var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) - Text("Hello, world!") + ScrollViewReader { proxy in + ScrollView { + VStack(alignment: .leading) { + TextEditor(text: $output) + .font(.system(.body, design: .monospaced)) + .frame(minWidth: 750, minHeight: 300) + .padding() + .disabled(true) + + Text("") + .id("bottom") + } + } + .onChange(of: output) { _, _ in + trimLinesIfNeeded() + withAnimation { + proxy.scrollTo("bottom", anchor: .bottom) + } + } + } + HStack { + Button(action: { + DispatchQueue.main.async { + self.showCancelButton = true + } + runShellCommandAsync(command: "msg machine init", outputText: $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) }) { + Text("Reinit") + } + Button(action: { + runShellCommandAsync(command: "msg machine start", outputText: $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 + }}) { + Text("Cancel") + } + } + if !showCancelButton{ + Button(action: { + runShellCommandAsync(command: "msg machine stop", outputText: $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) + + }) { + Text("Shell") + } + } + + } .padding() + .frame(width: 800, height: 400) } + + + private func trimLinesIfNeeded() { + let lines = 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 + } + } } #Preview { diff --git a/MSG Desktop/MSG_Desktop.entitlements b/MSG Desktop/MSG_Desktop.entitlements index 18aff0c..51a7cc5 100644 --- a/MSG Desktop/MSG_Desktop.entitlements +++ b/MSG Desktop/MSG_Desktop.entitlements @@ -3,8 +3,8 @@ com.apple.security.app-sandbox - + com.apple.security.files.user-selected.read-only - + diff --git a/MSG Desktop/MSG_DesktopApp.swift b/MSG Desktop/MSG_DesktopApp.swift index d5627be..9fe5e55 100644 --- a/MSG Desktop/MSG_DesktopApp.swift +++ b/MSG Desktop/MSG_DesktopApp.swift @@ -6,6 +6,96 @@ // import SwiftUI +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?) { + DispatchQueue.global(qos: .background).async { + let task = Process() + let pipe = Pipe() + + task.standardOutput = pipe + task.standardError = pipe + task.arguments = ["-c", command] + task.launchPath = "/bin/zsh" + task.standardInput = 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" + ] + + task.launch() + + let fileHandle = pipe.fileHandleForReading + fileHandle.waitForDataInBackgroundAndNotify() + + var observer: NSObjectProtocol? + + observer = NotificationCenter.default.addObserver(forName: .NSFileHandleDataAvailable, object: fileHandle, queue: nil) { _ in + let data = fileHandle.availableData + if let output = String(data: data, encoding: .utf8), !output.isEmpty { + DispatchQueue.main.async { + outputText.wrappedValue += output + } + } + fileHandle.waitForDataInBackgroundAndNotify() + } + + task.waitUntilExit() + + if let observer = observer { + NotificationCenter.default.removeObserver(observer) + } + + if showButton != nil { + showButton?.wrappedValue = false; + } + } +} + +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 + + 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" + ] + + task.launch() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: data, encoding: .utf8) ?? "Error reading output" + + return output.trimmingCharacters(in: .whitespacesAndNewlines) +} + @main struct MSG_DesktopApp: App {