modernize mac code

This commit is contained in:
Peter Steinberger 2025-06-20 12:40:29 +02:00
parent 26413645b3
commit a314fe8bf0
9 changed files with 86 additions and 19 deletions

View file

@ -0,0 +1,61 @@
import SwiftUI
// MARK: - Environment Keys
private struct ServerManagerKey: EnvironmentKey {
static let defaultValue: ServerManager? = nil
}
private struct NgrokServiceKey: EnvironmentKey {
static let defaultValue: NgrokService? = nil
}
private struct AppleScriptPermissionManagerKey: EnvironmentKey {
static let defaultValue: AppleScriptPermissionManager? = nil
}
private struct TerminalLauncherKey: EnvironmentKey {
static let defaultValue: TerminalLauncher? = nil
}
// MARK: - Environment Values Extensions
extension EnvironmentValues {
var serverManager: ServerManager {
get { self[ServerManagerKey.self] }
set { self[ServerManagerKey.self] = newValue }
}
var ngrokService: NgrokService {
get { self[NgrokServiceKey.self] }
set { self[NgrokServiceKey.self] = newValue }
}
var appleScriptPermissionManager: AppleScriptPermissionManager {
get { self[AppleScriptPermissionManagerKey.self] }
set { self[AppleScriptPermissionManagerKey.self] = newValue }
}
var terminalLauncher: TerminalLauncher {
get { self[TerminalLauncherKey.self] }
set { self[TerminalLauncherKey.self] = newValue }
}
}
// MARK: - View Extensions
extension View {
/// Injects all VibeTunnel services into the environment
func withVibeTunnelServices(
serverManager: ServerManager = .shared,
ngrokService: NgrokService = .shared,
appleScriptPermissionManager: AppleScriptPermissionManager = .shared,
terminalLauncher: TerminalLauncher = .shared
) -> some View {
self
.environment(\.serverManager, serverManager)
.environment(\.ngrokService, ngrokService)
.environment(\.appleScriptPermissionManager, appleScriptPermissionManager)
.environment(\.terminalLauncher, terminalLauncher)
}
}

View file

@ -1,6 +1,7 @@
import AppKit import AppKit
import Foundation import Foundation
import OSLog import OSLog
import Observation
/// Manages AppleScript automation permissions for VibeTunnel. /// Manages AppleScript automation permissions for VibeTunnel.
/// ///
@ -8,11 +9,12 @@ import OSLog
/// terminal applications via AppleScript. It provides continuous monitoring /// terminal applications via AppleScript. It provides continuous monitoring
/// and user-friendly permission request flows. /// and user-friendly permission request flows.
@MainActor @MainActor
final class AppleScriptPermissionManager: ObservableObject { @Observable
final class AppleScriptPermissionManager {
static let shared = AppleScriptPermissionManager() static let shared = AppleScriptPermissionManager()
@Published private(set) var hasPermission = false private(set) var hasPermission = false
@Published private(set) var isChecking = false private(set) var isChecking = false
private let logger = Logger( private let logger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "VibeTunnel", subsystem: Bundle.main.bundleIdentifier ?? "VibeTunnel",
@ -30,7 +32,7 @@ final class AppleScriptPermissionManager: ObservableObject {
} }
deinit { deinit {
monitoringTask?.cancel() // Task will be cancelled automatically when the object is deallocated
} }
/// Checks if we have AppleScript automation permissions. /// Checks if we have AppleScript automation permissions.

View file

@ -104,8 +104,8 @@ final class NgrokService: NgrokTunnelProtocol {
/// The ngrok process if using CLI mode /// The ngrok process if using CLI mode
private var ngrokProcess: Process? private var ngrokProcess: Process?
/// Timer for periodic status updates /// Task for periodic status updates
private var statusTimer: Timer? private var statusTask: Task<Void, Never>?
private let logger = Logger(subsystem: "sh.vibetunnel.vibetunnel", category: "NgrokService") private let logger = Logger(subsystem: "sh.vibetunnel.vibetunnel", category: "NgrokService")
@ -136,8 +136,8 @@ final class NgrokService: NgrokTunnelProtocol {
ngrokProcess = nil ngrokProcess = nil
} }
statusTimer?.invalidate() statusTask?.cancel()
statusTimer = nil statusTask = nil
isActive = false isActive = false
publicUrl = nil publicUrl = nil
@ -257,11 +257,12 @@ final class NgrokService: NgrokTunnelProtocol {
/// Monitors tunnel status periodically /// Monitors tunnel status periodically
private func startStatusMonitoring() { private func startStatusMonitoring() {
statusTimer?.invalidate() statusTask?.cancel()
statusTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in statusTask = Task { @MainActor in
Task { @MainActor in while !Task.isCancelled {
await self.updateTunnelStatus() await self.updateTunnelStatus()
try? await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds
} }
} }
} }

View file

@ -1,4 +1,3 @@
import Combine
import Foundation import Foundation
import Logging import Logging

View file

@ -21,7 +21,7 @@ struct DashboardSettingsView: View {
@State private var passwordError: String? @State private var passwordError: String?
@State private var passwordSaved = false @State private var passwordSaved = false
@StateObject private var permissionManager = AppleScriptPermissionManager.shared @State private var permissionManager = AppleScriptPermissionManager.shared
@State private var ngrokAuthToken = "" @State private var ngrokAuthToken = ""
@State private var ngrokStatus: NgrokTunnelStatus? @State private var ngrokStatus: NgrokTunnelStatus?

View file

@ -1,4 +1,3 @@
import Combine
import SwiftUI import SwiftUI
/// General settings tab for basic app preferences /// General settings tab for basic app preferences
@ -128,7 +127,7 @@ struct GeneralSettingsView: View {
// MARK: - Permissions Section // MARK: - Permissions Section
private struct PermissionsSection: View { private struct PermissionsSection: View {
@StateObject private var appleScriptManager = AppleScriptPermissionManager.shared @State private var appleScriptManager = AppleScriptPermissionManager.shared
@State private var accessibilityUpdateTrigger = 0 @State private var accessibilityUpdateTrigger = 0
private var hasAccessibilityPermission: Bool { private var hasAccessibilityPermission: Bool {

View file

@ -20,7 +20,7 @@ import SwiftUI
/// - ``AccessibilityPermissionManager`` for accessibility permissions /// - ``AccessibilityPermissionManager`` for accessibility permissions
/// - Terminal selection stored in UserDefaults /// - Terminal selection stored in UserDefaults
struct RequestPermissionsPageView: View { struct RequestPermissionsPageView: View {
@StateObject private var appleScriptManager = AppleScriptPermissionManager.shared @State private var appleScriptManager = AppleScriptPermissionManager.shared
@State private var accessibilityUpdateTrigger = 0 @State private var accessibilityUpdateTrigger = 0
private var hasAccessibilityPermission: Bool { private var hasAccessibilityPermission: Bool {

View file

@ -24,7 +24,7 @@ struct WelcomeView: View {
@AppStorage(AppConstants.UserDefaultsKeys.welcomeVersion) @AppStorage(AppConstants.UserDefaultsKeys.welcomeVersion)
private var welcomeVersion = 0 private var welcomeVersion = 0
@State private var cliInstaller = CLIInstaller() @State private var cliInstaller = CLIInstaller()
@StateObject private var permissionManager = AppleScriptPermissionManager.shared @State private var permissionManager = AppleScriptPermissionManager.shared
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {

View file

@ -9,6 +9,10 @@ struct VibeTunnelApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) @NSApplicationDelegateAdaptor(AppDelegate.self)
var appDelegate var appDelegate
@State private var sessionMonitor = SessionMonitor.shared @State private var sessionMonitor = SessionMonitor.shared
@State private var serverManager = ServerManager.shared
@State private var ngrokService = NgrokService.shared
@State private var appleScriptPermissionManager = AppleScriptPermissionManager.shared
@State private var terminalLauncher = TerminalLauncher.shared
init() { init() {
// No special initialization needed // No special initialization needed
@ -28,6 +32,7 @@ struct VibeTunnelApp: App {
// Welcome Window // Welcome Window
WindowGroup("Welcome", id: "welcome") { WindowGroup("Welcome", id: "welcome") {
WelcomeView() WelcomeView()
.withVibeTunnelServices()
} }
.windowResizability(.contentSize) .windowResizability(.contentSize)
.defaultSize(width: 580, height: 480) .defaultSize(width: 580, height: 480)
@ -35,6 +40,7 @@ struct VibeTunnelApp: App {
Settings { Settings {
SettingsView() SettingsView()
.withVibeTunnelServices()
} }
.commands { .commands {
CommandGroup(after: .appInfo) { CommandGroup(after: .appInfo) {
@ -55,7 +61,7 @@ struct VibeTunnelApp: App {
MenuBarExtra { MenuBarExtra {
MenuBarView() MenuBarView()
.environment(sessionMonitor) .environment(sessionMonitor)
.environment(serverMonitor) .withVibeTunnelServices()
} label: { } label: {
Image("menubar") Image("menubar")
.renderingMode(.template) .renderingMode(.template)
@ -159,7 +165,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser
// Check if server actually started // Check if server actually started
if serverManager.isRunning { if serverManager.isRunning {
logger.info("HTTP server started successfully on port \(self.serverManager.port)") logger.info("HTTP server started successfully on port \(self.serverManager.port)")
logger.info("Server mode: \(self.serverManager.serverMode.displayName)")
// Start monitoring sessions after server starts // Start monitoring sessions after server starts
sessionMonitor.startMonitoring() sessionMonitor.startMonitoring()