import AppKit final class AddServerSheet: NSViewController { var onComplete: ((SavedServer?) -> Void)? private var editing: SavedServer? private let hostField = NSTextField() private let portField: NSTextField = { let f = NSTextField() f.stringValue = "7878" return f }() private let authPicker = NSPopUpButton() private let nicknameField = NSTextField() private let usernameField = NSTextField() private let passwordField = NSSecureTextField() private let savePwCheckbox = NSButton(checkboxWithTitle: "Save password in Keychain", target: nil, action: nil) init(editing: SavedServer?) { self.editing = editing super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError() } override func loadView() { view = NSView(frame: NSRect(x: 0, y: 0, width: 380, height: 260)) } override func viewDidLoad() { super.viewDidLoad() buildUI() if let s = editing { hostField.stringValue = s.host portField.stringValue = "\(s.port)" authPicker.selectItem(withTitle: s.authMode == .guest ? "Guest" : "Account") nicknameField.stringValue = s.nickname ?? "" usernameField.stringValue = s.savedUsername ?? "" updateAuthVisibility() } } private func buildUI() { let title = NSTextField(labelWithString: editing == nil ? "Add Server" : "Edit Server") title.font = .boldSystemFont(ofSize: 14) let hostLabel = NSTextField(labelWithString: "Host:") let portLabel = NSTextField(labelWithString: "Port:") let authLabel = NSTextField(labelWithString: "Auth:") let nickLabel = NSTextField(labelWithString: "Nickname:") let userLabel = NSTextField(labelWithString: "Username:") let pwLabel = NSTextField(labelWithString: "Password:") hostField.placeholderString = "hostname or IP" hostField.setAccessibilityLabel("Server hostname or IP address") portField.setAccessibilityLabel("Server port") authPicker.addItem(withTitle: "Guest") authPicker.addItem(withTitle: "Account") authPicker.target = self; authPicker.action = #selector(authChanged) authPicker.setAccessibilityLabel("Authentication mode") nicknameField.placeholderString = "leave blank to use your system name" nicknameField.setAccessibilityLabel("Guest nickname (display name)") usernameField.placeholderString = "username" usernameField.setAccessibilityLabel("Username") passwordField.placeholderString = "leave blank to enter at connect" passwordField.setAccessibilityLabel("Password (optional — enter at connect time if blank)") savePwCheckbox.setAccessibilityLabel("Save password in system Keychain") let cancelButton = NSButton(title: "Cancel", target: self, action: #selector(cancelClicked)) cancelButton.bezelStyle = .rounded let saveButton = NSButton(title: "Save", target: self, action: #selector(saveClicked)) saveButton.bezelStyle = .rounded saveButton.keyEquivalent = "\r" saveButton.setAccessibilityLabel("Save server") let grid = NSGridView(views: [ [hostLabel, hostField], [portLabel, portField], [authLabel, authPicker], [nickLabel, nicknameField], [userLabel, usernameField], [pwLabel, passwordField], [NSView(), savePwCheckbox], ]) grid.rowSpacing = 8 grid.columnSpacing = 8 grid.column(at: 0).xPlacement = .trailing let buttonRow = NSStackView(views: [NSView(), cancelButton, saveButton]) buttonRow.orientation = .horizontal; buttonRow.spacing = 8 let stack = NSStackView(views: [title, grid, buttonRow]) stack.orientation = .vertical stack.spacing = 16 stack.edgeInsets = NSEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) stack.translatesAutoresizingMaskIntoConstraints = false view.addSubview(stack) NSLayoutConstraint.activate([ stack.topAnchor.constraint(equalTo: view.topAnchor), stack.leadingAnchor.constraint(equalTo: view.leadingAnchor), stack.trailingAnchor.constraint(equalTo: view.trailingAnchor), stack.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) updateAuthVisibility() } @objc private func authChanged() { updateAuthVisibility() } private func updateAuthVisibility() { let isAccount = authPicker.titleOfSelectedItem == "Account" nicknameField.isEnabled = !isAccount usernameField.isEnabled = isAccount passwordField.isEnabled = isAccount savePwCheckbox.isEnabled = isAccount } @objc private func saveClicked() { let host = hostField.stringValue.trimmingCharacters(in: .whitespaces) guard !host.isEmpty else { hostField.becomeFirstResponder(); return } let portStr = portField.stringValue.trimmingCharacters(in: .whitespaces) guard let port = UInt16(portStr), port > 0 else { portField.becomeFirstResponder(); return } let isGuest = authPicker.titleOfSelectedItem == "Guest" var server = editing ?? SavedServer(host: host, port: port, authMode: isGuest ? .guest : .password) server.host = host; server.port = port server.authMode = isGuest ? .guest : .password server.savedUsername = isGuest ? nil : usernameField.stringValue.trimmingCharacters(in: .whitespaces) let nick = nicknameField.stringValue.trimmingCharacters(in: .whitespaces) server.nickname = nick.isEmpty ? nil : nick if !isGuest && savePwCheckbox.state == .on { let pw = passwordField.stringValue if !pw.isEmpty { let tag = server.keychainTag ?? "voicecat.server.\(server.id.uuidString)" server.keychainTag = tag ServerListStore.savePassword(pw, tag: tag) } } else if isGuest { if let tag = server.keychainTag { ServerListStore.deletePassword(tag: tag) } server.keychainTag = nil } dismiss(nil) onComplete?(server) } @objc private func cancelClicked() { dismiss(nil) onComplete?(nil) } }