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() } } } } } }