PadXcode-Daemon/UI/DaemonSettingsView.swift

109 lines
5.4 KiB
Swift
Raw Permalink Normal View History

2026-04-12 00:42:51 -07:00
import SwiftUI
import ServiceManagement
struct DaemonSettingsView: View {
2026-04-12 22:14:13 -07:00
@ObservedObject var controller: DaemonController
2026-04-12 00:42:51 -07:00
@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)
2026-04-12 22:14:13 -07:00
.onAppear { pendingPort = String(preferences.port); preferences.refreshTailscaleIP() }
2026-04-12 00:42:51 -07:00
.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 }
}
}