108 lines
5.4 KiB
Swift
108 lines
5.4 KiB
Swift
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 }
|
|
}
|
|
}
|