diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index 6a26723..e68a8c4 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 961679322CFF117300B2B6DF /* NetworkExtension.framework */; }; 9616793D2CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 961679302CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = AA2C690E2D34F6920059AFAF /* LaunchAtLogin */; }; AA3B3DA92D2D23860099996A /* VPNLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3B3DA12D2D23860099996A /* VPNLib.framework */; }; AA3B3DBF2D2D23AB0099996A /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */; }; AA3B3DC12D2D23AB0099996A /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DC02D2D23AB0099996A /* SwiftProtobufPluginLibrary */; }; @@ -229,6 +230,7 @@ files = ( AA3B40A42D2FC8560099996A /* CoderSDK.framework in Frameworks */, AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */, + AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */, AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -368,7 +370,7 @@ buildRules = ( ); dependencies = ( - AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */, + AA2C698C2D354A800059AFAF /* PBXTargetDependency */, 9616793C2CFF117300B2B6DF /* PBXTargetDependency */, AA3B40A32D2FC8560099996A /* PBXTargetDependency */, ); @@ -379,6 +381,7 @@ packageProductDependencies = ( AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */, AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */, + AA2C690E2D34F6920059AFAF /* LaunchAtLogin */, ); productName = "Coder Desktop"; productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */; @@ -395,6 +398,7 @@ buildRules = ( ); dependencies = ( + AA2C698E2D354A840059AFAF /* PBXTargetDependency */, 961679112CFF100E00B2B6DF /* PBXTargetDependency */, AA3B40BA2D2FDA5C0099996A /* PBXTargetDependency */, ); @@ -421,6 +425,7 @@ buildRules = ( ); dependencies = ( + AA2C69902D354A880059AFAF /* PBXTargetDependency */, 9616791B2CFF100E00B2B6DF /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -445,6 +450,7 @@ buildRules = ( ); dependencies = ( + AA2C69922D354A8B0059AFAF /* PBXTargetDependency */, AA3B3DD02D2D249F0099996A /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -469,6 +475,7 @@ buildRules = ( ); dependencies = ( + AA2C69942D354A8E0059AFAF /* PBXTargetDependency */, AA3B40C32D2FE7760099996A /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -494,6 +501,7 @@ buildRules = ( ); dependencies = ( + AA2C69962D354A910059AFAF /* PBXTargetDependency */, AA3B3DAB2D2D23860099996A /* PBXTargetDependency */, AA3B3DAD2D2D23860099996A /* PBXTargetDependency */, ); @@ -520,6 +528,7 @@ buildRules = ( ); dependencies = ( + AA2C69982D354A940059AFAF /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( AA3B40922D2FC8560099996A /* CoderSDK */, @@ -542,6 +551,7 @@ buildRules = ( ); dependencies = ( + AA2C699A2D354A970059AFAF /* PBXTargetDependency */, AA3B409B2D2FC8560099996A /* PBXTargetDependency */, AA3B409D2D2FC8560099996A /* PBXTargetDependency */, ); @@ -607,11 +617,12 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */, - AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */, AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */, 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */, AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */, + AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */, + AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, ); preferredProjectObjectVersion = 77; productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */; @@ -764,6 +775,38 @@ target = 9616792F2CFF117300B2B6DF /* VPN */; targetProxy = 9616793B2CFF117300B2B6DF /* PBXContainerItemProxy */; }; + AA2C698C2D354A800059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C698E2D354A840059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69902D354A880059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69922D354A8B0059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69942D354A8E0059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69962D354A910059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69982D354A940059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C699A2D354A970059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */; + }; AA3B3DAB2D2D23860099996A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = AA3B3DA02D2D23860099996A /* VPNLib */; @@ -804,10 +847,6 @@ target = AA3B40902D2FC8560099996A /* CoderSDK */; targetProxy = AA3B40C22D2FE7760099996A /* PBXContainerItemProxy */; }; - AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -955,7 +994,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -987,7 +1026,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1006,7 +1045,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1025,7 +1064,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1043,7 +1082,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1061,7 +1100,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1091,7 +1130,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN"; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; @@ -1123,7 +1162,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN"; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; @@ -1157,7 +1196,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1194,7 +1233,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1216,7 +1255,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1233,7 +1272,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1265,7 +1304,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1302,7 +1341,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1324,7 +1363,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1341,7 +1380,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1446,6 +1485,22 @@ version = 1.28.2; }; }; + AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-modern"; + requirement = { + kind = exactVersion; + version = 1.1.0; + }; + }; + AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.58.0; + }; + }; AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WeTransfer/Mocker"; @@ -1462,14 +1517,6 @@ minimumVersion = 0.10.0; }; }; - AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.57.1; - }; - }; AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/lfroms/fluid-menu-bar-extra"; @@ -1489,6 +1536,51 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + AA2C690E2D34F6920059AFAF /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */; + productName = LaunchAtLogin; + }; + AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */ = { isa = XCSwiftPackageProductDependency; package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; @@ -1519,11 +1611,6 @@ package = AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; - AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */ = { - isa = XCSwiftPackageProductDependency; - package = AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; - productName = "plugin:SwiftLintBuildToolPlugin"; - }; AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */ = { isa = XCSwiftPackageProductDependency; package = AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */; diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5a69cd2..37e6bc5 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ec40e522ec1a2416e8e8f5cbe97424ab3e4a614e6ef453c10ea28e84e88b6771", + "originHash" : "c41f63aa01c78f450e2232efbefcd30874995ad120db77fa5942062d6f813891", "pins" : [ { "identity" : "fluid-menu-bar-extra", @@ -18,6 +18,15 @@ "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf" } }, + { + "identity" : "launchatlogin-modern", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin-modern", + "state" : { + "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc", + "version" : "1.1.0" + } + }, { "identity" : "mocker", "kind" : "remoteSourceControl", @@ -41,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", "state" : { - "revision" : "f9731bef175c3eea3a0ca960f1be78fcc2bc7853", - "version" : "0.57.1" + "revision" : "fac0c3d3ac69b15ea5382275dbbd5e583a2e05fa", + "version" : "0.58.0" } }, { diff --git a/Coder Desktop/Coder Desktop/About.swift b/Coder Desktop/Coder Desktop/About.swift index 4bff9d6..3771175 100644 --- a/Coder Desktop/Coder Desktop/About.swift +++ b/Coder Desktop/Coder Desktop/About.swift @@ -32,15 +32,7 @@ enum About { @MainActor static func open() { - #if compiler(>=5.9) && canImport(AppKit) - if #available(macOS 14, *) { - NSApp.activate() - } else { - NSApp.activate(ignoringOtherApps: true) - } - #else - NSApp.activate(ignoringOtherApps: true) - #endif + appActivate() NSApp.orderFrontStandardAboutPanel(options: [ .credits: credits, ]) diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 940f5cc..c45e632 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -14,6 +14,11 @@ struct DesktopApp: App { LoginForm().environmentObject(appDelegate.session) } .windowResizability(.contentSize) + SwiftUI.Settings { SettingsView() + .environmentObject(appDelegate.vpn) + .environmentObject(appDelegate.settings) + } + .windowResizability(.contentSize) } } @@ -22,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var menuBarExtra: FluidMenuBarExtra? let vpn: PreviewVPN let session: PreviewSession + let settings: Settings override init() { - // TODO: Replace with real implementations + // TODO: Replace with real implementation vpn = PreviewVPN() + settings = Settings() session = PreviewSession() } @@ -34,6 +41,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { VPNMenu().frame(width: 256) .environmentObject(self.vpn) .environmentObject(self.session) + .environmentObject(self.settings) } } @@ -49,3 +57,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { false } } + +@MainActor +func appActivate() { + NSApp.activate() +} diff --git a/Coder Desktop/Coder Desktop/Session.swift b/Coder Desktop/Coder Desktop/State.swift similarity index 70% rename from Coder Desktop/Coder Desktop/Session.swift rename to Coder Desktop/Coder Desktop/State.swift index 2e39ada..7a58808 100644 --- a/Coder Desktop/Coder Desktop/Session.swift +++ b/Coder Desktop/Coder Desktop/State.swift @@ -1,6 +1,8 @@ +import CoderSDK import Foundation import KeychainAccess import NetworkExtension +import SwiftUI protocol Session: ObservableObject { var hasSession: Bool { get } @@ -89,3 +91,47 @@ class SecureSession: ObservableObject, Session { static let sessionToken = "sessionToken" } } + +class Settings: ObservableObject { + private let store: UserDefaults + @AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false + + @Published var literalHeaders: [LiteralHeader] { + didSet { + try? store.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders) + } + } + + init(store: UserDefaults = .standard) { + self.store = store + _literalHeaders = Published( + initialValue: store.data( + forKey: Keys.literalHeaders + ).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? [] + ) + } + + enum Keys { + static let useLiteralHeaders = "UseLiteralHeaders" + static let literalHeaders = "LiteralHeaders" + } +} + +struct LiteralHeader: Hashable, Identifiable, Equatable, Codable { + var header: String + var value: String + var id: String { + "\(header):\(value)" + } + + init(header: String, value: String) { + self.header = header + self.value = value + } +} + +extension LiteralHeader { + func toSDKHeader() -> HTTPHeader { + return .init(header: header, value: value) + } +} diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index 776cee9..10fa6b0 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -3,6 +3,7 @@ import SwiftUI struct LoginForm: View { @EnvironmentObject var session: S + @EnvironmentObject var settings: Settings @Environment(\.dismiss) private var dismiss @State private var baseAccessURL: String = "" @@ -68,7 +69,7 @@ struct LoginForm: View { } loading = true defer { loading = false } - let client = Client(url: url, token: sessionToken) + let client = Client(url: url, token: sessionToken, headers: settings.literalHeaders.map { $0.toSDKHeader() }) do { _ = try await client.user("me") } catch { diff --git a/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift new file mode 100644 index 0000000..0c1bb9e --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift @@ -0,0 +1,16 @@ +import LaunchAtLogin +import SwiftUI + +struct GeneralTab: View { + var body: some View { + Form { + Section { + LaunchAtLogin.Toggle("Launch at Login") + } + }.formStyle(.grouped) + } +} + +#Preview { + GeneralTab() +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift new file mode 100644 index 0000000..9e2ea2a --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift @@ -0,0 +1,45 @@ +import SwiftUI + +struct LiteralHeaderModal: View { + var existingHeader: LiteralHeader? + + @EnvironmentObject var settings: Settings + @Environment(\.dismiss) private var dismiss + + @State private var header: String = "" + @State private var value: String = "" + + var body: some View { + VStack(spacing: 0) { + Form { + Section { + TextField("Header", text: $header) + TextField("Value", text: $value) + } + }.formStyle(.grouped).scrollDisabled(true).padding(.horizontal) + Divider() + HStack { + Spacer() + Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction) + Button(existingHeader == nil ? "Add" : "Save", action: submit) + .keyboardShortcut(.defaultAction) + }.padding(20) + }.onAppear { + if let existingHeader { + self.header = existingHeader.header + self.value = existingHeader.value + } + } + } + + func submit() { + defer { dismiss() } + if let existingHeader { + settings.literalHeaders.removeAll { $0 == existingHeader } + } + let newHeader = LiteralHeader(header: header, value: value) + if !settings.literalHeaders.contains(newHeader) { + settings.literalHeaders.append(newHeader) + } + } +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift new file mode 100644 index 0000000..aa27203 --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift @@ -0,0 +1,71 @@ +import SwiftUI + +struct LiteralHeadersSection: View { + @EnvironmentObject var vpn: VPN + @EnvironmentObject var settings: Settings + + @State private var selectedHeader: LiteralHeader.ID? + @State private var editingHeader: LiteralHeader? + @State private var addingNewHeader = false + + let inspection = Inspection() + + var body: some View { + Section { + Toggle(isOn: settings.$useLiteralHeaders) { + Text("HTTP Headers") + Text("When enabled, these headers will be included on all outgoing HTTP requests.") + if vpn.state != .disabled { Text("Cannot be modified while Coder VPN is enabled.") } + } + .controlSize(.large) + + Table(settings.literalHeaders, selection: $selectedHeader) { + TableColumn("Header", value: \.header) + TableColumn("Value", value: \.value) + }.opacity(settings.useLiteralHeaders ? 1 : 0.5) + .frame(minWidth: 400, minHeight: 200) + .padding(.bottom, 25) + .overlay(alignment: .bottom) { + VStack(alignment: .leading, spacing: 0) { + Divider() + HStack(spacing: 0) { + Button { + addingNewHeader = true + } label: { + Image(systemName: "plus") + .frame(width: 24, height: 24) + } + Divider() + Button { + settings.literalHeaders.removeAll { $0.id == selectedHeader } + selectedHeader = nil + } label: { + Image(systemName: "minus") + .frame(width: 24, height: 24) + }.disabled(selectedHeader == nil) + } + .buttonStyle(.borderless) + } + .background(.primary.opacity(0.04)) + .fixedSize(horizontal: false, vertical: true) + } + .background(.primary.opacity(0.04)) + .contextMenu(forSelectionType: LiteralHeader.ID.self, menu: { _ in }, + primaryAction: { selectedHeaders in + if let firstHeader = selectedHeaders.first { + editingHeader = settings.literalHeaders.first(where: { $0.id == firstHeader }) + } + }) + .disabled(!settings.useLiteralHeaders) + } + .sheet(isPresented: $addingNewHeader) { + LiteralHeaderModal() + } + .sheet(item: $editingHeader) { header in + LiteralHeaderModal(existingHeader: header) + }.onTapGesture { + selectedHeader = nil + }.disabled(vpn.state != .disabled) + .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector + } +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift new file mode 100644 index 0000000..5e1d7ef --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift @@ -0,0 +1,14 @@ +import SwiftUI + +struct NetworkTab: View { + var body: some View { + Form { + LiteralHeadersSection() + } + .formStyle(.grouped) + } +} + +#Preview { + NetworkTab() +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift new file mode 100644 index 0000000..8aac9a0 --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift @@ -0,0 +1,26 @@ +import SwiftUI + +struct SettingsView: View { + @AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general + + var body: some View { + TabView(selection: $selection) { + GeneralTab() + .tabItem { + Label("General", systemImage: "gearshape") + }.tag(SettingsTab.general) + NetworkTab() + .tabItem { + Label("Network", systemImage: "dot.radiowaves.left.and.right") + }.tag(SettingsTab.network) + }.frame(width: 600) + .frame(maxHeight: 500) + .scrollContentBackground(.hidden) + .fixedSize() + } +} + +enum SettingsTab: Int { + case general + case network +} diff --git a/Coder Desktop/Coder Desktop/Views/Util.swift b/Coder Desktop/Coder Desktop/Views/Util.swift index 96fb246..ce61c66 100644 --- a/Coder Desktop/Coder Desktop/Views/Util.swift +++ b/Coder Desktop/Coder Desktop/Views/Util.swift @@ -1,4 +1,5 @@ import Combine +import SwiftUI // This is required for inspecting stateful views final class Inspection { diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index 8788936..5ba76e0 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -3,6 +3,7 @@ import SwiftUI struct VPNMenu: View { @EnvironmentObject var vpn: VPN @EnvironmentObject var session: S + @Environment(\.openSettings) private var openSettings let inspection = Inspection() @@ -21,6 +22,8 @@ struct VPNMenu: View { )) { Text("CoderVPN") .frame(maxWidth: .infinity, alignment: .leading) + .font(.body.bold()) + .foregroundColor(.primary) }.toggleStyle(.switch) .disabled(vpnDisabled) } @@ -50,6 +53,12 @@ struct VPNMenu: View { TrayDivider() } AuthButton() + Button { + openSettings() + appActivate() + } label: { + ButtonRowView { Text("Settings") } + }.buttonStyle(.plain) Button { About.open() } label: { diff --git a/Coder Desktop/Coder Desktop/Windows.swift b/Coder Desktop/Coder Desktop/Windows.swift index e82680c..61ac4ef 100644 --- a/Coder Desktop/Coder Desktop/Windows.swift +++ b/Coder Desktop/Coder Desktop/Windows.swift @@ -8,15 +8,7 @@ enum Windows: String { extension OpenWindowAction { // Type-safe wrapper for opening windows that also focuses the new window func callAsFunction(id: Windows) { - #if compiler(>=5.9) && canImport(AppKit) - if #available(macOS 14, *) { - NSApp.activate() - } else { - NSApp.activate(ignoringOtherApps: true) - } - #else - NSApp.activate(ignoringOtherApps: true) - #endif + appActivate() callAsFunction(id: id.rawValue) // The arranging behaviour is flakey without this NSApp.arrangeInFront(nil) diff --git a/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift new file mode 100644 index 0000000..6d68a74 --- /dev/null +++ b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift @@ -0,0 +1,48 @@ +@testable import Coder_Desktop +import SwiftUI +import Testing +import ViewInspector + +@MainActor +@Suite(.timeLimit(.minutes(1))) +struct LiteralHeadersSettingTests { + let vpn: MockVPNService + let sut: LiteralHeadersSection + let view: any View + + init() { + vpn = MockVPNService() + sut = LiteralHeadersSection() + let store = UserDefaults(suiteName: #file)! + store.removePersistentDomain(forName: #file) + view = sut.environmentObject(vpn).environmentObject(Settings(store: store)) + } + + @Test + func testToggleDisabledWhenVPNEnabled() async throws { + vpn.state = .connected + + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in + let toggle = try view.find(ViewType.Toggle.self) + #expect(toggle.isDisabled()) + #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") } + } + } + } + + @Test + func testToggleEnabledWhenVPNDisabled() async throws { + vpn.state = .disabled + + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in + let toggle = try view.find(ViewType.Toggle.self) + #expect(!toggle.isDisabled()) + #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") } + } + } + } + + // TODO: More tests, ViewInspector cannot currently inspect Tables +} diff --git a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift index 912f409..6ba1154 100644 --- a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift +++ b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift @@ -15,7 +15,9 @@ struct LoginTests { init() { session = MockSession() sut = LoginForm() - view = sut.environmentObject(session) + let store = UserDefaults(suiteName: #file)! + store.removePersistentDomain(forName: #file) + view = sut.environmentObject(session).environmentObject(Settings(store: store)) } @Test @@ -70,12 +72,11 @@ struct LoginTests { @Test func testFailedAuthentication() async throws { - let login = LoginForm() let url = URL(string: "https://testFailedAuthentication.com")! Mock(url: url.appendingPathComponent("/api/v2/users/me"), statusCode: 401, data: [.get: Data()]).register() - try await ViewHosting.host(login.environmentObject(session)) { - try await login.inspection.inspect { view in + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in try view.find(ViewType.TextField.self).setInput(url.absoluteString) try view.find(button: "Next").tap() #expect(throws: Never.self) { try view.find(text: "Session Token") } diff --git a/Makefile b/Makefile index b746639..a428f06 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,7 @@ test: -testPlan $(SCHEME) \ -skipPackagePluginValidation \ CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO \ - | LC_ALL="en_US.UTF-8" xcpretty + CODE_SIGNING_ALLOWED=NO | xcbeautify lint: swiftlint \