78 lines
4.2 KiB
Swift
78 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
struct ContentView: View {
|
|
@EnvironmentObject private var model: ComposeModel
|
|
var body: some View {
|
|
NavigationSplitView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Image(systemName: "shippingbox.fill").foregroundStyle(.blue)
|
|
Text("Services").font(.headline)
|
|
Spacer()
|
|
Text("\(model.services.count)").foregroundStyle(.secondary)
|
|
}.padding(.horizontal)
|
|
List(selection: $model.selectedService) {
|
|
ForEach(model.services) { service in
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(service.id).font(.headline)
|
|
Text(service.image).font(.caption).foregroundStyle(.secondary).lineLimit(1)
|
|
}.padding(.vertical, 4).tag(service.id)
|
|
}
|
|
}
|
|
Text(model.projectURL?.lastPathComponent ?? "No project opened")
|
|
.font(.caption).foregroundStyle(.secondary).lineLimit(1).padding()
|
|
}
|
|
.navigationSplitViewColumnWidth(min: 220, ideal: 265)
|
|
} detail: {
|
|
VStack(alignment: .leading, spacing: 14) {
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("Container Compose").font(.title2.bold())
|
|
Text(model.projectURL?.deletingLastPathComponent().path ?? "Open a Compose project to get started")
|
|
.font(.caption).foregroundStyle(.secondary).textSelection(.enabled)
|
|
}
|
|
Spacer()
|
|
Text(model.status).foregroundStyle(.secondary)
|
|
}
|
|
if let service = model.services.first(where: { $0.id == model.selectedService }) {
|
|
GroupBox("Selected service") {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
LabeledContent("Name", value: service.id)
|
|
LabeledContent("Image", value: service.image)
|
|
LabeledContent("Ports", value: service.ports.joined(separator: ", ").isEmpty ? "None" : service.ports.joined(separator: ", "))
|
|
LabeledContent("Depends on", value: service.dependencies.joined(separator: ", ").isEmpty ? "None" : service.dependencies.joined(separator: ", "))
|
|
}.frame(maxWidth: .infinity, alignment: .leading).padding(5)
|
|
}
|
|
}
|
|
HStack {
|
|
Text("Command output").font(.headline)
|
|
Spacer()
|
|
Button("Clear") { model.clearLog() }
|
|
}
|
|
ScrollView {
|
|
Text(model.output).font(.system(size: 12, design: .monospaced))
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.textSelection(.enabled).padding(12)
|
|
}
|
|
.background(.black.opacity(0.87), in: RoundedRectangle(cornerRadius: 9))
|
|
.foregroundStyle(.green)
|
|
}.padding(20)
|
|
}
|
|
.toolbar {
|
|
ToolbarItemGroup {
|
|
Button("Open YAML", systemImage: "folder") { model.openProject() }
|
|
Button("Up", systemImage: "play.fill") { model.perform("up") }.disabled(model.busy || model.projectURL == nil)
|
|
Button("Down", systemImage: "stop.fill") { model.perform("down") }.disabled(model.busy || model.projectURL == nil)
|
|
Button("Restart", systemImage: "arrow.clockwise") { model.perform("restart") }.disabled(model.busy || model.projectURL == nil)
|
|
Button("Status", systemImage: "list.bullet.rectangle") { model.perform("ps") }.disabled(model.busy || model.projectURL == nil)
|
|
Button("Logs", systemImage: "text.alignleft") { model.perform("logs") }.disabled(model.busy || model.projectURL == nil)
|
|
Menu("More") {
|
|
Button("Pull Images") { model.perform("pull") }
|
|
Button("Build Images") { model.perform("build") }
|
|
Button("Check Runtime") { model.checkRuntime() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|