Some additional info @eskimo
import SwiftUI
let interactive = [
"zsh",
"bash",
"vi",
"vim",
"top"
]
struct REPLView: View {
@AppStorage("launch") var launchPath: String = "/bin/zsh"
@State var context: Text = Text("SHELL STARTED AT ")
.fontWeight(.black) + Text("\(Date().ISO8601Format())\n")
.foregroundColor(.accentColor)
.fontWeight(.bold)
@State var command: String = ""
var body: some View {
VStack {
ScrollView([.horizontal, .vertical]) {
HStack {
context
Spacer()
}
.frame(width: 500)
.padding()
}
.frame(width: 500, height: 300)
.border(.gray)
HStack {
TextField("@\(launchPath)", text: $command)
.onSubmit {
runCommand()
}
.textFieldStyle(.plain)
Button {
runCommand()
} label: {
Label("Send", systemImage: "arrow.right")
.foregroundColor(.accentColor)
}
.buttonStyle(.plain)
}
.padding()
}
.onAppear {
newPrompt()
}
}
func newPrompt() {
var new: Text {
let new = Text("Utilities REPL@*\(Host.current().name!)* \(launchPath)")
.fontWeight(.bold)
let prompt = Text(" > ")
return context + new + prompt
}
context = new
}
func runCommand() {
if interactive.contains(command
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: "\t", with: "")
.replacingOccurrences(of: "\n", with: "")) {
var new: Text {
let commnd = Text(" \(command)\n")
let cont: Text = Text("*The REPL have not yet supported `\(command)` inside the environment.*\n")
.foregroundColor(.red)
return context + commnd + cont
}
context = new
newPrompt()
} else {
var new: Text {
let commnd = Text(" \(command)\n")
let cont: Text = Text(run(command: command))
return context + commnd + cont
}
context = new
newPrompt()
}
}
}
import Foundation
struct Utility: Codable, Hashable, Identifiable {
let id: UUID
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Utility.self, from: data)
else { return nil }
self.name = result.name
self.asyncFetch = result.asyncFetch
self.symbol = result.symbol
self.command = result.command
self.id = result.id
}
public init(name: String? = nil, command: String? = nil, symbol: String? = nil, asyncFetch: Bool? = nil) {
self.name = name ?? "New Utility"
self.command = command ?? #"echo "Command "# + (name ?? "New Utility") + #" Executed""#
self.symbol = symbol ?? symbols.randomElement()!
self.asyncFetch = asyncFetch ?? true
self.id = .init()
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.command = try container.decode(String.self, forKey: .command)
self.symbol = try container.decode(String.self, forKey: .symbol)
self.asyncFetch = try container.decode(Bool.self, forKey: .asyncFetch)
self.id = try container.decode(UUID.self, forKey: .id)
}
public init?(_ script: URL) {
guard let content = try? String(contentsOf: script) else {
return nil
}
var scriptText = ""
for i in content.split(separator: "\n") {
var j: String = String(i)
while i.first == " " || i.first == "\t" {
j.removeFirst()
}
while i.last == " " || i.last == "\t" {
j.removeLast()
}
if j.first == "#" {
continue
}
scriptText += (j + "; ")
}
self.name = script.deletingPathExtension().lastPathComponent
self.command = scriptText
self.symbol = symbols.randomElement()!
self.asyncFetch = true
self.id = .init()
}
public var rawValue: String {
var encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let result = String(data: data, encoding: .utf8)
else {
return ""
}
return result
}
var name: String
var command: String
var symbol: String
var asyncFetch: Bool
@discardableResult
func run(launchPath: String? = nil) -> String {
let output = Utilities.run(command: command)
return output
}
func run(logFile: inout String, launchPath: String? = nil, stripeDeadCharacters: Bool? = nil) {
var output = Utilities.run(command: command)
if stripeDeadCharacters ?? false {
while output.last == "\n" {
output.removeLast()
}
}
logFile += output
}
}
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else { return nil }
self = result
}
public var rawValue: String {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}