Native macOS agent that keeps the office VPN and the NAS shares working without the shell wrapper it replaces. - VPN: full port of the `ovpn` script — host-route pinning, gateway discovery via networksetup, tunnel liveness by address plus pushed route, startup grace, restart of a wedged daemon. Root steps are batched into one system authorization prompt. - SMB: mounts via NetFS, discovers shares from the server, detects mounts event-driven through NSWorkspace, matches the host by resolved alias so externally created mounts are recognised. - Credentials never pass through the process: the VPN auth file is written by `security` straight to disk, and the password is stored via SecItemAdd rather than a command line visible in ps. - UI: status window, settings editor, ⌘+/⌘− zoom that re-lays out rather than scaling a bitmap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
39 lines
1.3 KiB
Swift
39 lines
1.3 KiB
Swift
import SwiftUI
|
|
|
|
/// A ScrollView that is as tall as its content, up to a cap.
|
|
///
|
|
/// Exists because a plain `ScrollView` inside a `VStack` has a tiny ideal
|
|
/// height: given only `.frame(maxHeight:)` it collapses to nearly nothing. That
|
|
/// bug shipped twice — once emptying the share list, once emptying the whole
|
|
/// settings window — so the fix lives in one place now.
|
|
/// File scope, not nested: a generic type cannot hold the static storage a
|
|
/// PreferenceKey needs.
|
|
private struct HeightKey: PreferenceKey {
|
|
static let defaultValue: CGFloat = 0
|
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
value = max(value, nextValue())
|
|
}
|
|
}
|
|
|
|
struct MeasuredScrollView<Content: View>: View {
|
|
var minHeight: CGFloat = 0
|
|
var maxHeight: CGFloat
|
|
@ViewBuilder var content: Content
|
|
|
|
@State private var measured: CGFloat = 0
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
content
|
|
.background(
|
|
GeometryReader { geometry in
|
|
Color.clear.preference(
|
|
key: HeightKey.self, value: geometry.size.height)
|
|
}
|
|
)
|
|
}
|
|
.frame(height: min(max(measured, minHeight), maxHeight))
|
|
.onPreferenceChange(HeightKey.self) { measured = $0 }
|
|
}
|
|
}
|