import AppKit import VoiceCatCore final class PerUserTuningSheet: NSViewController { private let client: VoiceCatClient private let userId: UInt32 private let nickname: String private var streams: [StreamSummary] = [] private var rows: [StreamRow] = [] private struct StreamRow { let streamId: UInt32 let label: String let gainSlider: NSSlider let muteCheckbox: NSButton let nrCheckbox: NSButton } init(client: VoiceCatClient, userId: UInt32, nickname: String) { self.client = client self.userId = userId self.nickname = nickname super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError() } override func loadView() { view = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 200)) } override func viewDidLoad() { super.viewDidLoad() streams = client.listUserStreams(userId) buildUI() } private func buildUI() { let titleLabel = NSTextField(labelWithString: "Volume & Noise Settings — \(nickname)") titleLabel.font = .boldSystemFont(ofSize: 13) var rowViews: [NSView] = [titleLabel] for stream in streams { let (_, state) = client.getRemoteStream(userId: userId, streamId: stream.id) let gain = state?.gain ?? 1.0 let muted = state?.muted ?? false let nr = state?.noiseReduction ?? false let gainSlider = NSSlider(value: Double(gain * 100), minValue: 0, maxValue: 200, target: self, action: #selector(sliderChanged)) gainSlider.tag = Int(stream.id) gainSlider.numberOfTickMarks = 0 gainSlider.setAccessibilityLabel("Volume for \(stream.label): \(Int(gain * 100)) percent") let muteCheckbox = NSButton(checkboxWithTitle: "Mute", target: self, action: #selector(muteChanged)) muteCheckbox.state = muted ? .on : .off muteCheckbox.tag = Int(stream.id) muteCheckbox.setAccessibilityLabel("Mute \(stream.label)") let nrCheckbox = NSButton(checkboxWithTitle: "Noise reduction", target: self, action: #selector(nrChanged)) nrCheckbox.state = nr ? .on : .off nrCheckbox.tag = Int(stream.id) nrCheckbox.setAccessibilityLabel("Noise reduction for \(stream.label)") let streamLabel = NSTextField(labelWithString: "\(stream.label):") let gainLabel = NSTextField(labelWithString: "Volume:") let row = NSStackView(views: [streamLabel, gainLabel, gainSlider, muteCheckbox, nrCheckbox]) row.orientation = .horizontal; row.spacing = 8 rowViews.append(row) rows.append(StreamRow(streamId: stream.id, label: stream.label, gainSlider: gainSlider, muteCheckbox: muteCheckbox, nrCheckbox: nrCheckbox)) } if streams.isEmpty { rowViews.append(NSTextField(labelWithString: "This user has no active streams.")) } let doneButton = NSButton(title: "Done", target: self, action: #selector(doneClicked)) doneButton.bezelStyle = .rounded; doneButton.keyEquivalent = "\r" doneButton.setAccessibilityLabel("Close settings") rowViews.append(NSStackView(views: [NSView(), doneButton])) let stack = NSStackView(views: rowViews) stack.orientation = .vertical stack.spacing = 10 stack.edgeInsets = NSEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) 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), ]) } @objc private func sliderChanged(_ sender: NSSlider) { apply(streamId: UInt32(sender.tag)) } @objc private func muteChanged(_ sender: NSButton) { apply(streamId: UInt32(sender.tag)) } @objc private func nrChanged(_ sender: NSButton) { apply(streamId: UInt32(sender.tag)) } private func apply(streamId: UInt32) { guard let row = rows.first(where: { $0.streamId == streamId }) else { return } let gain = Float(row.gainSlider.doubleValue) / 100.0 let muted = row.muteCheckbox.state == .on let nr = row.nrCheckbox.state == .on client.setRemoteStream(userId: userId, streamId: streamId, gain: gain, muted: muted, noiseReduction: nr) row.gainSlider.setAccessibilityLabel("Volume for \(row.label): \(Int(row.gainSlider.doubleValue)) percent") } @objc private func doneClicked() { dismiss(nil) } }