mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
modernize mac code
This commit is contained in:
parent
26413645b3
commit
a314fe8bf0
9 changed files with 86 additions and 19 deletions
61
VibeTunnel/Core/Extensions/EnvironmentValues+Services.swift
Normal file
61
VibeTunnel/Core/Extensions/EnvironmentValues+Services.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import Combine
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Logging
|
import Logging
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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?
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue