import SwiftUI struct ProfileEditorView: View { @Environment(\.dismiss) var dismiss @EnvironmentObject var store: ProfileStore @State var profile: ConnectionProfile var isNew: Bool var onSave: (ConnectionProfile) -> Void @State private var password: String = "" @State private var savePassword: Bool = false @State private var storedPasswordPresent: Bool = false init(profile: ConnectionProfile, isNew: Bool, onSave: @escaping (ConnectionProfile) -> Void) { _profile = State(initialValue: profile) self.isNew = isNew self.onSave = onSave } var liveWarnings: [String] { ProfileValidation.warnings(for: profile) } var body: some View { VStack(spacing: 0) { Text(isNew ? "Neues Profil" : "Profil bearbeiten").font(.headline).padding() Form { Section("Allgemein") { TextField("Name", text: $profile.name) TextField("Gruppe", text: $profile.group) Picker("Protokoll", selection: $profile.proto) { ForEach(ProtocolType.allCases) { p in Text(p.rawValue).tag(p) } } .onChange(of: profile.proto) { _, new in if profile.port == 0 { profile.port = new.defaultPort } } Picker("Farb-Tag", selection: $profile.colorTag) { Text("Blau").tag("blue"); Text("Rot").tag("red") Text("Grün").tag("green"); Text("Orange").tag("orange") Text("Lila").tag("purple") } } Section("Verbindung") { TextField("Host", text: $profile.host) .autocorrectionDisabled() TextField("Port", value: $profile.port, format: .number) TextField("Benutzername", text: $profile.username) .autocorrectionDisabled() // Passwort direkt unter dem Benutzernamen (Keychain-gespeichert) if storedPasswordPresent && password.isEmpty { HStack { Image(systemName: "checkmark.circle.fill").foregroundStyle(.green) Text("Passwort gespeichert") Spacer() Button("Löschen", role: .destructive) { deleteStoredPassword() } } } SecureField(storedPasswordPresent ? "Neues Passwort …" : "Passwort", text: $password) Toggle("Passwort in Keychain speichern", isOn: $savePassword) TextField("Auflösung (RDP, z. B. 1920x1080 oder Automatisch)", text: $profile.resolution) TextField("Extra-Argumente", text: $profile.extraArgs) if !liveWarnings.isEmpty { ForEach(liveWarnings, id: \.self) { w in HStack(spacing: 6) { Image(systemName: "exclamationmark.triangle").foregroundStyle(.orange) Text(w).font(.caption).foregroundStyle(.orange) } } } } Section("SSH-Key (optional)") { Picker("SSH-Key", selection: $profile.sshKeyID) { Text("Kein Key (Passwort-Login)").tag(UUID?.none) ForEach(store.sshKeys) { k in Text(k.name).tag(UUID?.some(k.id)) } } Text(sshpassHint).font(.caption).foregroundStyle(.secondary) } } .formStyle(.grouped) HStack { Button("Abbrechen", role: .cancel) { dismiss() } Spacer() Button("Speichern") { var toSave = ProfileValidation.cleaned(profile) if toSave.port == 0 { toSave.port = toSave.proto.defaultPort } if toSave.passwordAccount == nil { toSave.passwordAccount = "profile-\(toSave.id.uuidString)" } if savePassword, !password.isEmpty, let acct = toSave.passwordAccount { _ = KeychainHelper.save(password, account: acct) } onSave(toSave) dismiss() } .keyboardShortcut(.defaultAction) .disabled(profile.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || profile.host.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) } .padding() } .frame(width: 520, height: 680) .onAppear { if let acct = profile.passwordAccount, let stored = KeychainHelper.load(account: acct), !stored.isEmpty { storedPasswordPresent = true savePassword = true } } } var sshpassHint: String { if SSHService.findSshpass() != nil { return "sshpass gefunden → Auto-Login mit Keychain-Passwort aktiv, kein Tippen nötig." } else { return "Ohne sshpass: Passwort wird beim Verbinden im Terminal abgefragt. Für Auto-Login: brew install sshpass." } } func deleteStoredPassword() { if let acct = profile.passwordAccount { KeychainHelper.delete(account: acct) } password = "" savePassword = false storedPasswordPresent = false } }