import SwiftUI import AppKit /// Settings panel (pushed from the menu-bar panel). struct SettingsView: View { @EnvironmentObject private var settings: AppSettings var body: some View { Form { Section("SparkControl backend") { TextField("Base URL", text: $settings.backendBaseURL) .textFieldStyle(.roundedBorder) Toggle("Skip TLS verification (self-signed cert)", isOn: $settings.skipTLSVerification) } Section("Call detection") { Toggle("Auto-record when a call is detected", isOn: $settings.autoRecordOnDetection) Text("Detects Zoom, Teams, Signal, and Google Meet (any browser).") .font(.caption) .foregroundStyle(.secondary) } Section("Transcription") { TextField("Your name", text: $settings.selfName) .textFieldStyle(.roundedBorder) Toggle("Auto-send recordings to backend", isOn: $settings.autoSendOnStop) Toggle("Build readable recap (topics + highlights)", isOn: $settings.recapEnabled) HStack { Picker("Default recap template", selection: $settings.defaultTemplateId) { ForEach(settings.recapTemplates) { Text($0.name).tag($0.id) } } Button("Manage…") { TemplatesWindow.shared.show(settings: settings) } } Text("Your name labels your mic channel. Auto-send transcribes on stop; the recap writes transcript.md + recap.html. Templates define the takeaways categories per meeting type.") .font(.caption) .foregroundStyle(.secondary) } Section("Output") { HStack { Text(settings.outputFolderPath) .lineLimit(1) .truncationMode(.middle) .foregroundStyle(.secondary) Spacer() Button("Choose…", action: chooseFolder) } } Section("Adapters") { Text("Inert in Phase 0 — these toggles only persist for now.") .font(.caption) .foregroundStyle(.secondary) ForEach(AppSettings.adapterKeys, id: \.key) { adapter in Toggle(adapter.label, isOn: binding(for: adapter.key)) } } } .formStyle(.grouped) .frame(width: 320) .navigationTitle("Settings") } private func binding(for key: String) -> Binding { Binding( get: { settings.adapterEnabled[key] ?? true }, set: { settings.adapterEnabled[key] = $0 } ) } private func chooseFolder() { let panel = NSOpenPanel() panel.canChooseDirectories = true panel.canChooseFiles = false panel.allowsMultipleSelection = false panel.prompt = "Choose" panel.directoryURL = settings.outputFolderURL // The app is a menu-bar accessory and this is invoked from the transient // MenuBarExtra(.window) popover. Use the async begin(...) API rather than // runModal() — a nested modal loop can let the popover dismiss the panel // out from under it. Activate first so the panel comes to the front. NSApp.activate(ignoringOtherApps: true) panel.begin { response in guard response == .OK, let url = panel.url else { return } settings.outputFolderPath = url.path } } }