import SwiftUI import ServiceManagement struct DaemonSettingsView: View { @Bindable var controller: DaemonController @ObservedObject var preferences: AppPreferences @State private var pendingPort = "" @State private var showRestartAlert = false var body: some View { TabView { serverTab.tabItem { Label("Server", systemImage:"server.rack") } buildTab.tabItem { Label("Build", systemImage:"hammer") } generalTab.tabItem { Label("General", systemImage:"gearshape") } } .padding(20).frame(width:520) .onAppear { pendingPort = String(preferences.port) } .alert("Restart Required", isPresented:$showRestartAlert) { Button("Restart Now") { Task { await controller.applyPortChange() } } Button("Later", role:.cancel) {} } message: { Text("Port change takes effect after restart.") } } private var serverTab: some View { Form { Section { HStack { TextField("Port", text:$pendingPort).textFieldStyle(.roundedBorder).frame(width:80).onSubmit { applyPort() } Text("Default: 8080").foregroundStyle(.secondary).font(.caption) Spacer() Button("Apply") { applyPort() }.disabled(pendingPort == String(preferences.port)) } HStack { Text("Tailscale IP").foregroundStyle(.secondary); Spacer() Text(preferences.tailscaleIP).font(.system(.body,design:.monospaced)) Button { NSPasteboard.general.clearContents(); NSPasteboard.general.setString(preferences.tailscaleIP,forType:.string) } label: { Image(systemName:"doc.on.clipboard") }.buttonStyle(.plain).foregroundStyle(.secondary) } HStack { Text("Status").foregroundStyle(.secondary); Spacer() HStack(spacing:6) { Circle().fill(statusColor).frame(width:8,height:8) Text(controller.serverState.statusLabel) } } HStack(spacing:8) { Button(controller.serverState.isRunning ? "Stop" : "Start") { Task { if controller.serverState.isRunning { await controller.stop() } else { await controller.start() } } }.tint(controller.serverState.isRunning ? .red : .green) Button("Restart") { Task { await controller.restart() } }.disabled(!controller.serverState.isRunning) }.buttonStyle(.bordered) } header: { Text("Network & Status") } }.formStyle(.grouped) } private var buildTab: some View { Form { Section { HStack { TextField("XXXXXXXXXX", text:$preferences.developmentTeam) .textFieldStyle(.roundedBorder).font(.system(.body,design:.monospaced)).frame(width:140) Button("Find It") { NSWorkspace.shared.open(URL(string:"https://developer.apple.com/account#MembershipDetailsCard")!) } .buttonStyle(.link).font(.caption) } if preferences.developmentTeam.isEmpty { Label("Required for code signing.", systemImage:"exclamationmark.triangle.fill").foregroundStyle(.orange).font(.caption) } else if preferences.developmentTeam.count == 10 { Label("Team ID looks valid.", systemImage:"checkmark.circle.fill").foregroundStyle(.green).font(.caption) } } header: { Text("Development Team ID") } footer: { Text("Found at developer.apple.com/account → Membership → Team ID") } Section { Toggle("Allow Provisioning Updates", isOn:$preferences.allowProvisioningUpdates) Picker("Configuration", selection:$preferences.buildConfiguration) { Text("Debug").tag("Debug"); Text("Release").tag("Release") } } header: { Text("Build Options") } }.formStyle(.grouped) } private var generalTab: some View { Form { Section { Toggle("Launch at Login", isOn:Binding(get:{ preferences.launchAtLogin }, set:{ preferences.launchAtLogin = $0 })) Toggle("Show in Dock", isOn:$preferences.showInDock) } header: { Text("Startup") } footer: { Text("Launch at Login is managed by macOS System Settings.") } Section { Button("Open System Login Items") { SMAppService.openSystemSettingsLoginItems() }.buttonStyle(.link) } header: { Text("System Settings") } Section { LabeledContent("Version", value:"1.0.0") LabeledContent("Vapor", value:"4.x") LabeledContent("Platform", value:"macOS Sequoia") } header: { Text("About") } }.formStyle(.grouped) } private var statusColor: Color { switch controller.serverState { case .running: return .green; case .starting: return .yellow; case .error: return .red; default: return .secondary } } private func applyPort() { guard let p = Int(pendingPort), p > 1024, p < 65535, p != preferences.port else { return } preferences.port = p if controller.serverState.isRunning { showRestartAlert = true } } }