WP-4c: returns list + settings per SPEC-A2b

Add ReturnsList (newest-first, max 8, click-to-Finder, hidden when empty)
and SettingsView (hotkey row, folder Choose… via FolderSettings + AppModel
seam mutators, async updateWatchFolder(url) only).
This commit is contained in:
2026-08-31 16:07:41 +04:00
parent 9989333c24
commit 66657ac532
2 changed files with 151 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
import AppKit
import SwiftUI
import ShotdeckCore
struct SettingsView: View {
@Environment(AppModel.self) private var model
var body: some View {
Form {
Section("Hotkey") {
LabeledContent("Capture", value: "⌥⇧2 — fixed in this version")
}
Section("Folders") {
LabeledContent("Watch folder") {
HStack(spacing: 8) {
Text(model.watchFolderURL.path)
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(.secondary)
Button("Choose…") { model.chooseWatchFolder() }
}
}
LabeledContent("Output folder") {
HStack(spacing: 8) {
Text(model.outboxURL.path)
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(.secondary)
Button("Choose…") { model.chooseOutboxFolder() }
}
}
}
Section {
Button("Reveal spool folder") { model.openSpoolFolder() }
}
}
.padding()
.frame(width: 360)
.controlSize(.small)
}
}
extension AppModel: SettingsWindowPresenting {
private static var settingsWindowController: NSWindowController?
public func presentSettingsWindow() {
if let existing = Self.settingsWindowController {
existing.window?.makeKeyAndOrderFront(nil)
NSApp.activate()
return
}
let hosting = NSHostingController(rootView: SettingsView().environment(self))
let window = NSWindow(contentViewController: hosting)
window.title = "Shotdeck Settings"
window.styleMask = [.titled, .closable]
window.isReleasedWhenClosed = false
window.center()
let controller = NSWindowController(window: window)
Self.settingsWindowController = controller
controller.showWindow(nil)
NSApp.activate()
}
func chooseOutboxFolder() {
guard let url = chooseDirectory(startingAt: outboxURL) else { return }
FolderSettings.setOutbox(url)
setFolderURLs(outbox: url, watch: watchFolderURL)
setStatus("Output folder set to \(url.lastPathComponent).")
}
func chooseWatchFolder() {
guard let url = chooseDirectory(startingAt: watchFolderURL) else { return }
FolderSettings.setWatchFolder(url)
setFolderURLs(outbox: outboxURL, watch: url)
Task {
do {
try await watcher.updateWatchFolder(url)
setStatus("Watch folder set to \(url.lastPathComponent).")
} catch {
setStatus(
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
)
}
}
}
private func chooseDirectory(startingAt directory: URL) -> URL? {
let panel = NSOpenPanel()
panel.canChooseDirectories = true
panel.canChooseFiles = false
panel.allowsMultipleSelection = false
panel.canCreateDirectories = true
panel.prompt = "Choose"
panel.directoryURL = directory
guard panel.runModal() == .OK else { return nil }
return panel.url
}
}