mirror of
https://github.com/acamarata/curtain.git
synced 2026-06-30 18:54:25 +00:00
Detection: netstat lives at /usr/sbin/netstat, not /usr/bin — the hardcoded wrong path silently killed the ESTABLISHED-TCP activator (root cause of the failed live test). Fixed and live-verified. Added peered-UDP activator (5900-5902) for High-Performance sessions, per-signal transition logging, unconditional error logging for dead probe helpers, and probe v2 with full CGSession dictionary diffing. 7 new parser tests (32 total). Fixes from a full audit + adversarial review: idle source setting honored (default now Remote session activity), cover scope reduced to a coherent two-mode model with legacy migration (per-display toggle was inverted in onlyMarked and dead in all), curtain test no longer schedules a teardown over a live session, specific-display password box placement gets a real picker, refuse-to-arm enforced, activation notification posts a real banner, menu password gate bypassed when the event tap is dead, shared single-decoder aerial player with stale-task guard and async playability check, password buffer zeroed on successful unlock and Esc, XPC interruption/invalidation handlers, modern Accessibility settings URL, launchPath modernized, codesign failures now abort release.sh, monotonic CFBundleVersion, install.sh temp cleanup, dead armDisarmHotkey setting removed. Refactor: Curtain.swift and PreferencesWindow.swift split into focused files (largest now 479 lines). Wiki, README, and contributing docs updated to match. Build clean at 0 warnings, 32/32 tests pass.
54 lines
2.6 KiB
Swift
54 lines
2.6 KiB
Swift
import SwiftUI
|
|
|
|
/// Purpose: General tab — master switch, login-item, menu-bar toggle, activation
|
|
/// timing, and manual test actions. Extracted from PreferencesView to keep
|
|
/// every tab file under 500 lines.
|
|
/// Inputs: @AppStorage bindings shared with the headless coordinator, plus injected
|
|
/// action closures for side-effectful buttons.
|
|
/// Outputs: writes to UserDefaults; calls LoginItem.set and onMenuBarToggle.
|
|
/// Constraints: @MainActor (SwiftUI); changes are live because AppStorage shares the
|
|
/// same UserDefaults domain as the coordinator.
|
|
/// SPORT: MASTER-PREFS
|
|
struct PrefGeneralTab: View {
|
|
@AppStorage(Settings.Key.armed) private var armed = true
|
|
@AppStorage(Settings.Key.launchAtLogin) private var launchAtLogin = true
|
|
@AppStorage(Settings.Key.showInMenuBar) private var showInMenuBar = true
|
|
@AppStorage(Settings.Key.onStartActivate) private var onStartActivate = true
|
|
@AppStorage(Settings.Key.connectGraceSeconds) private var connectGraceSeconds = 2
|
|
@AppStorage(Settings.Key.notifyOnActivate) private var notifyOnActivate = true
|
|
@AppStorage(Settings.Key.playSoundOnActivate) private var playSoundOnActivate = false
|
|
|
|
let activateNow: () -> Void
|
|
let testNow: () -> Void
|
|
let onMenuBarToggle: (Bool) -> Void
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("Status") {
|
|
Toggle("Armed (master switch)", isOn: $armed)
|
|
Toggle("Open at login", isOn: $launchAtLogin)
|
|
.onChange(of: launchAtLogin) { LoginItem.set($0) }
|
|
Toggle("Show in menu bar", isOn: $showInMenuBar)
|
|
.onChange(of: showInMenuBar) { onMenuBarToggle($0) }
|
|
}
|
|
Section("Activation") {
|
|
Toggle("Activate curtain when a remote session begins", isOn: $onStartActivate)
|
|
Stepper("Connect grace: \(connectGraceSeconds)s", value: $connectGraceSeconds, in: 0...30)
|
|
Toggle("Log a note when the curtain activates", isOn: $notifyOnActivate)
|
|
Toggle("Play a sound when the curtain activates", isOn: $playSoundOnActivate)
|
|
}
|
|
Section {
|
|
HStack {
|
|
Button("Activate Now", action: activateNow)
|
|
Button("Test (10s)", action: testNow)
|
|
}
|
|
} header: {
|
|
Text("Manual")
|
|
} footer: {
|
|
Text("Test runs a 10 second curtain so you can check appearance and reveal.")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
}
|
|
}
|