Show in Mac status bar plus Login Item (#773)
Add a status menu via SwiftUI MenuBarExtra where to: - Show/hide app - Launch on login via "Login Item" target - Toggle profiles on/off Only weird that the login item is not added to the list of "Open at Login", but to "Allow in the Background", see https://github.com/pilotmoon/Scroll-Reverser/issues/165 Requires some refactoring to bring AppContext initialization to the AppDelegate. Fixes #617 Fixes #482 Fixes #696 Fixes #505
This commit is contained in:
parent
d60ab97922
commit
41de48789e
|
@ -7,6 +7,9 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
0E757F132CD0CFFC006E13E1 /* PassepartoutLoginItemApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E757F122CD0CFFC006E13E1 /* PassepartoutLoginItemApp.swift */; };
|
||||||
|
0E757F202CD0D22B006E13E1 /* PassepartoutLoginItem.app in Embed Login Item */ = {isa = PBXBuildFile; fileRef = 0E757F102CD0CFFC006E13E1 /* PassepartoutLoginItem.app */; platformFilters = (macos, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
0E757F232CD0D2BD006E13E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E757F212CD0D2B7006E13E1 /* AppDelegate.swift */; };
|
||||||
0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; };
|
0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; };
|
||||||
0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; };
|
0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; };
|
||||||
0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; };
|
0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; };
|
||||||
|
@ -29,6 +32,13 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
0E757F242CD0D812006E13E1 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 0E06D1872B87629100176E1D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 0E757F0F2CD0CFFC006E13E1;
|
||||||
|
remoteInfo = PassepartoutLoginItem;
|
||||||
|
};
|
||||||
0EC332D02B8A1808000B9C2F /* PBXContainerItemProxy */ = {
|
0EC332D02B8A1808000B9C2F /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 0E06D1872B87629100176E1D /* Project object */;
|
containerPortal = 0E06D1872B87629100176E1D /* Project object */;
|
||||||
|
@ -46,6 +56,17 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
0E757F1F2CD0D1FB006E13E1 /* Embed Login Item */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = Contents/Library/LoginItems;
|
||||||
|
dstSubfolderSpec = 1;
|
||||||
|
files = (
|
||||||
|
0E757F202CD0D22B006E13E1 /* PassepartoutLoginItem.app in Embed Login Item */,
|
||||||
|
);
|
||||||
|
name = "Embed Login Item";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */ = {
|
0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -72,6 +93,10 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
0E06D18F2B87629100176E1D /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E06D18F2B87629100176E1D /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
0E757F102CD0CFFC006E13E1 /* PassepartoutLoginItem.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PassepartoutLoginItem.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
0E757F122CD0CFFC006E13E1 /* PassepartoutLoginItemApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassepartoutLoginItemApp.swift; sourceTree = "<group>"; };
|
||||||
|
0E757F182CD0CFFD006E13E1 /* LoginItem.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LoginItem.entitlements; sourceTree = "<group>"; };
|
||||||
|
0E757F212CD0D2B7006E13E1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.xctestplan; sourceTree = "<group>"; };
|
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.xctestplan; sourceTree = "<group>"; };
|
||||||
0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
|
0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
|
||||||
|
@ -99,6 +124,13 @@
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
0E757F0D2CD0CFFC006E13E1 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
0EC332C52B8A1808000B9C2F /* Frameworks */ = {
|
0EC332C52B8A1808000B9C2F /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -141,6 +173,7 @@
|
||||||
0E06D18F2B87629100176E1D /* Passepartout.app */,
|
0E06D18F2B87629100176E1D /* Passepartout.app */,
|
||||||
0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */,
|
0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */,
|
||||||
0EDE56F02CABE42E0082D21C /* PassepartoutIntents.appex */,
|
0EDE56F02CABE42E0082D21C /* PassepartoutIntents.appex */,
|
||||||
|
0E757F102CD0CFFC006E13E1 /* PassepartoutLoginItem.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -153,6 +186,16 @@
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
0E757F112CD0CFFC006E13E1 /* LoginItem */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0E757F212CD0D2B7006E13E1 /* AppDelegate.swift */,
|
||||||
|
0E757F122CD0CFFC006E13E1 /* PassepartoutLoginItemApp.swift */,
|
||||||
|
0E757F182CD0CFFD006E13E1 /* LoginItem.entitlements */,
|
||||||
|
);
|
||||||
|
path = LoginItem;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0E7E3D592B9345FD002BBDB4 /* Passepartout */ = {
|
0E7E3D592B9345FD002BBDB4 /* Passepartout */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -160,6 +203,7 @@
|
||||||
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */,
|
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */,
|
||||||
0E7E3D5A2B9345FD002BBDB4 /* App */,
|
0E7E3D5A2B9345FD002BBDB4 /* App */,
|
||||||
0EDE56E82CABE40D0082D21C /* Intents */,
|
0EDE56E82CABE40D0082D21C /* Intents */,
|
||||||
|
0E757F112CD0CFFC006E13E1 /* LoginItem */,
|
||||||
0E7E3D612B9345FD002BBDB4 /* Shared */,
|
0E7E3D612B9345FD002BBDB4 /* Shared */,
|
||||||
0E7E3D652B9345FD002BBDB4 /* Tunnel */,
|
0E7E3D652B9345FD002BBDB4 /* Tunnel */,
|
||||||
0EBE80DD2BF55C9100E36A20 /* Library */,
|
0EBE80DD2BF55C9100E36A20 /* Library */,
|
||||||
|
@ -240,15 +284,17 @@
|
||||||
0ED27CBF2B9331FF0089E26B /* Frameworks */,
|
0ED27CBF2B9331FF0089E26B /* Frameworks */,
|
||||||
0E06D18D2B87629100176E1D /* Resources */,
|
0E06D18D2B87629100176E1D /* Resources */,
|
||||||
0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */,
|
0EC332D62B8A1808000B9C2F /* Embed Foundation Extensions */,
|
||||||
0E8D852E2C328C54005493DE /* SwiftLint */,
|
|
||||||
0EDE56FE2CABE42E0082D21C /* Embed ExtensionKit Extensions */,
|
0EDE56FE2CABE42E0082D21C /* Embed ExtensionKit Extensions */,
|
||||||
|
0E757F1F2CD0D1FB006E13E1 /* Embed Login Item */,
|
||||||
|
0E8D852E2C328C54005493DE /* SwiftLint */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
0E6C0A032BF4047100450362 /* PBXTargetDependency */,
|
0E6C0A032BF4047100450362 /* PBXTargetDependency */,
|
||||||
0EC332D12B8A1808000B9C2F /* PBXTargetDependency */,
|
|
||||||
0EDE56F92CABE42E0082D21C /* PBXTargetDependency */,
|
0EDE56F92CABE42E0082D21C /* PBXTargetDependency */,
|
||||||
|
0E757F252CD0D812006E13E1 /* PBXTargetDependency */,
|
||||||
|
0EC332D12B8A1808000B9C2F /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Passepartout;
|
name = Passepartout;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
@ -258,6 +304,23 @@
|
||||||
productReference = 0E06D18F2B87629100176E1D /* Passepartout.app */;
|
productReference = 0E06D18F2B87629100176E1D /* Passepartout.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
0E757F0F2CD0CFFC006E13E1 /* PassepartoutLoginItem */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 0E757F1E2CD0CFFD006E13E1 /* Build configuration list for PBXNativeTarget "PassepartoutLoginItem" */;
|
||||||
|
buildPhases = (
|
||||||
|
0E757F0C2CD0CFFC006E13E1 /* Sources */,
|
||||||
|
0E757F0D2CD0CFFC006E13E1 /* Frameworks */,
|
||||||
|
0E757F0E2CD0CFFC006E13E1 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = PassepartoutLoginItem;
|
||||||
|
productName = PassepartoutLoginItem;
|
||||||
|
productReference = 0E757F102CD0CFFC006E13E1 /* PassepartoutLoginItem.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */ = {
|
0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */;
|
buildConfigurationList = 0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */;
|
||||||
|
@ -309,6 +372,9 @@
|
||||||
0E06D18E2B87629100176E1D = {
|
0E06D18E2B87629100176E1D = {
|
||||||
CreatedOnToolsVersion = 15.2;
|
CreatedOnToolsVersion = 15.2;
|
||||||
};
|
};
|
||||||
|
0E757F0F2CD0CFFC006E13E1 = {
|
||||||
|
CreatedOnToolsVersion = 15.4;
|
||||||
|
};
|
||||||
0EC332C72B8A1808000B9C2F = {
|
0EC332C72B8A1808000B9C2F = {
|
||||||
CreatedOnToolsVersion = 15.2;
|
CreatedOnToolsVersion = 15.2;
|
||||||
};
|
};
|
||||||
|
@ -332,6 +398,7 @@
|
||||||
targets = (
|
targets = (
|
||||||
0E06D18E2B87629100176E1D /* Passepartout */,
|
0E06D18E2B87629100176E1D /* Passepartout */,
|
||||||
0EDE56EF2CABE42E0082D21C /* PassepartoutIntents */,
|
0EDE56EF2CABE42E0082D21C /* PassepartoutIntents */,
|
||||||
|
0E757F0F2CD0CFFC006E13E1 /* PassepartoutLoginItem */,
|
||||||
0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */,
|
0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -350,6 +417,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
0E757F0E2CD0CFFC006E13E1 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
0EC332C62B8A1808000B9C2F /* Resources */ = {
|
0EC332C62B8A1808000B9C2F /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -401,6 +475,15 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
0E757F0C2CD0CFFC006E13E1 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
0E757F232CD0D2BD006E13E1 /* AppDelegate.swift in Sources */,
|
||||||
|
0E757F132CD0CFFC006E13E1 /* PassepartoutLoginItemApp.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
0EC332C42B8A1808000B9C2F /* Sources */ = {
|
0EC332C42B8A1808000B9C2F /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -429,6 +512,14 @@
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
productRef = 0E6C0A042BF4047600450362 /* TunnelLibrary */;
|
productRef = 0E6C0A042BF4047600450362 /* TunnelLibrary */;
|
||||||
};
|
};
|
||||||
|
0E757F252CD0D812006E13E1 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
platformFilters = (
|
||||||
|
macos,
|
||||||
|
);
|
||||||
|
target = 0E757F0F2CD0CFFC006E13E1 /* PassepartoutLoginItem */;
|
||||||
|
targetProxy = 0E757F242CD0D812006E13E1 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
0EC332D12B8A1808000B9C2F /* PBXTargetDependency */ = {
|
0EC332D12B8A1808000B9C2F /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */;
|
target = 0EC332C72B8A1808000B9C2F /* PassepartoutTunnel */;
|
||||||
|
@ -692,6 +783,54 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
0E757F1C2CD0CFFD006E13E1 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Passepartout/LoginItem/LoginItem.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
CURRENT_PROJECT_VERSION = 3645;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)";
|
||||||
|
INFOPLIST_KEY_LSBackgroundOnly = YES;
|
||||||
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "$(CFG_COPYRIGHT)";
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks";
|
||||||
|
MARKETING_VERSION = 3.0.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_LOGIN_ITEM_ID)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SUPPORTED_PLATFORMS = macosx;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
0E757F1D2CD0CFFD006E13E1 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Passepartout/LoginItem/LoginItem.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
CURRENT_PROJECT_VERSION = 3645;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)";
|
||||||
|
INFOPLIST_KEY_LSBackgroundOnly = YES;
|
||||||
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "$(CFG_COPYRIGHT)";
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks";
|
||||||
|
MARKETING_VERSION = 3.0.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(CFG_LOGIN_ITEM_ID)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SUPPORTED_PLATFORMS = macosx;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
0EC332D42B8A1808000B9C2F /* Debug */ = {
|
0EC332D42B8A1808000B9C2F /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -820,6 +959,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
0E757F1E2CD0CFFD006E13E1 /* Build configuration list for PBXNativeTarget "PassepartoutLoginItem" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
0E757F1C2CD0CFFD006E13E1 /* Debug */,
|
||||||
|
0E757F1D2CD0CFFD006E13E1 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */ = {
|
0EC332D32B8A1808000B9C2F /* Build configuration list for PBXNativeTarget "PassepartoutTunnel" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1540"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
buildArchitectures = "Automatic">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "0E757F0F2CD0CFFC006E13E1"
|
||||||
|
BuildableName = "PassepartoutLoginItem.app"
|
||||||
|
BlueprintName = "PassepartoutLoginItem"
|
||||||
|
ReferencedContainer = "container:Passepartout.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "0E757F0F2CD0CFFC006E13E1"
|
||||||
|
BuildableName = "PassepartoutLoginItem.app"
|
||||||
|
BlueprintName = "PassepartoutLoginItem"
|
||||||
|
ReferencedContainer = "container:Passepartout.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "0E757F0F2CD0CFFC006E13E1"
|
||||||
|
BuildableName = "PassepartoutLoginItem.app"
|
||||||
|
BlueprintName = "PassepartoutLoginItem"
|
||||||
|
ReferencedContainer = "container:Passepartout.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -14,10 +14,12 @@
|
||||||
<string>$(CFG_IAP_BUNDLE_PREFIX)</string>
|
<string>$(CFG_IAP_BUNDLE_PREFIX)</string>
|
||||||
<key>keychainGroupId</key>
|
<key>keychainGroupId</key>
|
||||||
<string>$(CFG_KEYCHAIN_GROUP_ID)</string>
|
<string>$(CFG_KEYCHAIN_GROUP_ID)</string>
|
||||||
<key>tunnelId</key>
|
|
||||||
<string>$(CFG_TUNNEL_ID)</string>
|
|
||||||
<key>legacyV2CloudKitId</key>
|
<key>legacyV2CloudKitId</key>
|
||||||
<string>$(CFG_LEGACY_V2_CLOUDKIT_ID)</string>
|
<string>$(CFG_LEGACY_V2_CLOUDKIT_ID)</string>
|
||||||
|
<key>loginItemId</key>
|
||||||
|
<string>$(CFG_LOGIN_ITEM_ID)</string>
|
||||||
|
<key>tunnelId</key>
|
||||||
|
<string>$(CFG_TUNNEL_ID)</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
|
@ -50,10 +52,6 @@
|
||||||
</array>
|
</array>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSUserActivityTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>CustomIntentIntent</string>
|
|
||||||
</array>
|
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>remote-notification</string>
|
<string>remote-notification</string>
|
||||||
|
|
|
@ -23,4 +23,33 @@
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import AppUI
|
||||||
|
import CommonLibrary
|
||||||
|
import PassepartoutKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class AppDelegate: NSObject {
|
||||||
|
|
||||||
|
@AppStorage(AppPreference.confirmsQuit.key)
|
||||||
|
var confirmsQuit = true
|
||||||
|
|
||||||
|
let context: AppContext = .shared
|
||||||
|
// let context: AppContext = .mock(withRegistry: .shared)
|
||||||
|
|
||||||
|
func configure() {
|
||||||
|
PassepartoutConfiguration.shared.configureLogging(
|
||||||
|
to: BundleConfiguration.urlForAppLog,
|
||||||
|
parameters: Constants.shared.log,
|
||||||
|
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
|
||||||
|
)
|
||||||
|
AppUI.configure(with: context)
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
// keep this for login item because scenePhase is not triggered
|
||||||
|
Task {
|
||||||
|
try await context.tunnel.prepare(purge: true)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "StatusActive@2x.png",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
Passepartout/App/Assets.xcassets/Menu/MenuActive.imageset/StatusActive@2x.png
vendored
Normal file
BIN
Passepartout/App/Assets.xcassets/Menu/MenuActive.imageset/StatusActive@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 817 B |
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "StatusInactive@2x.png",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
Passepartout/App/Assets.xcassets/Menu/MenuInactive.imageset/StatusInactive@2x.png
vendored
Normal file
BIN
Passepartout/App/Assets.xcassets/Menu/MenuInactive.imageset/StatusInactive@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 743 B |
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "StatusPending@2x.png",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
Passepartout/App/Assets.xcassets/Menu/MenuPending.imageset/StatusPending@2x.png
vendored
Normal file
BIN
Passepartout/App/Assets.xcassets/Menu/MenuPending.imageset/StatusPending@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -42,8 +42,9 @@ struct PassepartoutApp: App {
|
||||||
@Environment(\.scenePhase)
|
@Environment(\.scenePhase)
|
||||||
private var scenePhase
|
private var scenePhase
|
||||||
|
|
||||||
private let context: AppContext = .shared
|
private var context: AppContext {
|
||||||
// private let context: AppContext = .mock(withRegistry: .shared)
|
appDelegate.context
|
||||||
|
}
|
||||||
|
|
||||||
private let appName = BundleConfiguration.mainDisplayName
|
private let appName = BundleConfiguration.mainDisplayName
|
||||||
|
|
||||||
|
@ -62,12 +63,19 @@ struct PassepartoutApp: App {
|
||||||
#else
|
#else
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
Window(appName, id: appName, content: contentView)
|
Window(appName, id: appName, content: contentView)
|
||||||
.defaultSize(width: 600.0, height: 400.0)
|
.defaultSize(width: 600, height: 400)
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
SettingsView(profileManager: context.profileManager)
|
SettingsView(profileManager: context.profileManager)
|
||||||
.frame(minWidth: 300, minHeight: 200)
|
.frame(minWidth: 300, minHeight: 200)
|
||||||
}
|
}
|
||||||
|
MenuBarExtra {
|
||||||
|
AppMenu()
|
||||||
|
.withEnvironment(from: context, theme: theme)
|
||||||
|
} label: {
|
||||||
|
AppMenuImage(connectionObserver: context.connectionObserver)
|
||||||
|
.environmentObject(theme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -79,14 +87,6 @@ private extension PassepartoutApp {
|
||||||
tunnel: context.tunnel,
|
tunnel: context.tunnel,
|
||||||
registry: context.registry
|
registry: context.registry
|
||||||
)
|
)
|
||||||
.onLoad {
|
|
||||||
PassepartoutConfiguration.shared.configureLogging(
|
|
||||||
to: BundleConfiguration.urlForAppLog,
|
|
||||||
parameters: Constants.shared.log,
|
|
||||||
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
|
|
||||||
)
|
|
||||||
AppUI.configure(with: context)
|
|
||||||
}
|
|
||||||
.onChange(of: scenePhase) {
|
.onChange(of: scenePhase) {
|
||||||
switch $0 {
|
switch $0 {
|
||||||
case .active:
|
case .active:
|
||||||
|
|
|
@ -28,7 +28,11 @@
|
||||||
import AppUI
|
import AppUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
extension AppDelegate: UIApplicationDelegate {
|
||||||
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||||
|
configure()
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,17 +27,12 @@
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
import AppUI
|
import AppUI
|
||||||
import CommonLibrary
|
|
||||||
import PassepartoutKit
|
import PassepartoutKit
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
||||||
|
|
||||||
@AppStorage(AppPreference.confirmsQuit.key)
|
|
||||||
private var confirmsQuit = true
|
|
||||||
|
|
||||||
|
extension AppDelegate: NSApplicationDelegate {
|
||||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
NSApp.windows[0].styleMask.remove(.closable)
|
configureAppWindow()
|
||||||
|
configure()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||||
|
@ -52,8 +47,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
|
||||||
private extension AppDelegate {
|
private extension AppDelegate {
|
||||||
|
var isStartedFromLoginItem: Bool {
|
||||||
|
NSApp.isHidden
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureAppWindow() {
|
||||||
|
if isStartedFromLoginItem {
|
||||||
|
AppWindow.shared.isVisible = false
|
||||||
|
}
|
||||||
|
AppWindow.shared.removeCloseButton()
|
||||||
|
}
|
||||||
|
|
||||||
func quitConfirmationAlert() -> NSApplication.TerminateReply {
|
func quitConfirmationAlert() -> NSApplication.TerminateReply {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.alertStyle = .warning
|
alert.alertStyle = .warning
|
||||||
|
|
|
@ -39,6 +39,7 @@ CFG_GROUP_ID[sdk=macosx*] = $(CFG_TEAM_ID).$(CFG_RAW_GROUP_ID)
|
||||||
CFG_KEYCHAIN_GROUP_ID = $(CFG_TEAM_ID).$(CFG_RAW_GROUP_ID)
|
CFG_KEYCHAIN_GROUP_ID = $(CFG_TEAM_ID).$(CFG_RAW_GROUP_ID)
|
||||||
CFG_IAP_BUNDLE_PREFIX = com.algoritmico.ios.Passepartout
|
CFG_IAP_BUNDLE_PREFIX = com.algoritmico.ios.Passepartout
|
||||||
CFG_INTENTS_ID = $(CFG_APP_ID).Intents
|
CFG_INTENTS_ID = $(CFG_APP_ID).Intents
|
||||||
|
CFG_LOGIN_ITEM_ID = $(CFG_APP_ID).LoginItem
|
||||||
CFG_RAW_GROUP_ID = group.com.algoritmico.Passepartout
|
CFG_RAW_GROUP_ID = group.com.algoritmico.Passepartout
|
||||||
CFG_TEAM_ID = DTDYD63ZX9
|
CFG_TEAM_ID = DTDYD63ZX9
|
||||||
CFG_TUNNEL_ID = $(CFG_APP_ID).Tunnel
|
CFG_TUNNEL_ID = $(CFG_APP_ID).Tunnel
|
||||||
|
|
|
@ -38,6 +38,16 @@ public enum Strings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public enum AppMenu {
|
||||||
|
public enum Items {
|
||||||
|
/// Launch on Login
|
||||||
|
public static let launchOnLogin = Strings.tr("Localizable", "app_menu.items.launch_on_login", fallback: "Launch on Login")
|
||||||
|
/// Quit %@
|
||||||
|
public static func quit(_ p1: Any) -> String {
|
||||||
|
return Strings.tr("Localizable", "app_menu.items.quit", String(describing: p1), fallback: "Quit %@")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public enum Entities {
|
public enum Entities {
|
||||||
public enum ConnectionStatus {
|
public enum ConnectionStatus {
|
||||||
/// Connected
|
/// Connected
|
||||||
|
@ -227,6 +237,8 @@ public enum Strings {
|
||||||
public static let gateway = Strings.tr("Localizable", "global.gateway", fallback: "Gateway")
|
public static let gateway = Strings.tr("Localizable", "global.gateway", fallback: "Gateway")
|
||||||
/// General
|
/// General
|
||||||
public static let general = Strings.tr("Localizable", "global.general", fallback: "General")
|
public static let general = Strings.tr("Localizable", "global.general", fallback: "General")
|
||||||
|
/// Hide
|
||||||
|
public static let hide = Strings.tr("Localizable", "global.hide", fallback: "Hide")
|
||||||
/// Hostname
|
/// Hostname
|
||||||
public static let hostname = Strings.tr("Localizable", "global.hostname", fallback: "Hostname")
|
public static let hostname = Strings.tr("Localizable", "global.hostname", fallback: "Hostname")
|
||||||
/// Interface
|
/// Interface
|
||||||
|
@ -297,6 +309,8 @@ public enum Strings {
|
||||||
public static let servers = Strings.tr("Localizable", "global.servers", fallback: "Servers")
|
public static let servers = Strings.tr("Localizable", "global.servers", fallback: "Servers")
|
||||||
/// Settings
|
/// Settings
|
||||||
public static let settings = Strings.tr("Localizable", "global.settings", fallback: "Settings")
|
public static let settings = Strings.tr("Localizable", "global.settings", fallback: "Settings")
|
||||||
|
/// Show
|
||||||
|
public static let show = Strings.tr("Localizable", "global.show", fallback: "Show")
|
||||||
/// Status
|
/// Status
|
||||||
public static let status = Strings.tr("Localizable", "global.status", fallback: "Status")
|
public static let status = Strings.tr("Localizable", "global.status", fallback: "Status")
|
||||||
/// Storage
|
/// Storage
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"global.folder" = "Folder";
|
"global.folder" = "Folder";
|
||||||
"global.gateway" = "Gateway";
|
"global.gateway" = "Gateway";
|
||||||
"global.general" = "General";
|
"global.general" = "General";
|
||||||
|
"global.hide" = "Hide";
|
||||||
"global.hostname" = "Hostname";
|
"global.hostname" = "Hostname";
|
||||||
"global.interface" = "Interface";
|
"global.interface" = "Interface";
|
||||||
"global.keep_alive" = "Keep-alive";
|
"global.keep_alive" = "Keep-alive";
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
"global.server" = "Server";
|
"global.server" = "Server";
|
||||||
"global.servers" = "Servers";
|
"global.servers" = "Servers";
|
||||||
"global.settings" = "Settings";
|
"global.settings" = "Settings";
|
||||||
|
"global.show" = "Show";
|
||||||
"global.status" = "Status";
|
"global.status" = "Status";
|
||||||
"global.storage" = "Storage";
|
"global.storage" = "Storage";
|
||||||
"global.subnet" = "Subnet";
|
"global.subnet" = "Subnet";
|
||||||
|
@ -233,6 +235,11 @@
|
||||||
"providers.vpn.preset" = "Preset";
|
"providers.vpn.preset" = "Preset";
|
||||||
"providers.vpn.no_servers" = "No servers";
|
"providers.vpn.no_servers" = "No servers";
|
||||||
|
|
||||||
|
// MARK: - App menu
|
||||||
|
|
||||||
|
"app_menu.items.launch_on_login" = "Launch on Login";
|
||||||
|
"app_menu.items.quit" = "Quit %@";
|
||||||
|
|
||||||
// MARK: - Components
|
// MARK: - Components
|
||||||
|
|
||||||
"ui.connection_status.on_demand_suffix" = " (on-demand)";
|
"ui.connection_status.on_demand_suffix" = " (on-demand)";
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// AppMenu+Model.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import PassepartoutKit
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
|
extension AppMenu {
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class Model: ObservableObject {
|
||||||
|
private let appService: SMAppService
|
||||||
|
|
||||||
|
var isVisible: Bool {
|
||||||
|
get {
|
||||||
|
AppWindow.shared.isVisible
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
AppWindow.shared.isVisible = newValue
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var launchesOnLogin: Bool {
|
||||||
|
get {
|
||||||
|
appService.status == .enabled
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
do {
|
||||||
|
if newValue {
|
||||||
|
try appService.register()
|
||||||
|
} else {
|
||||||
|
try appService.unregister()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
pp_log(.app, .error, "Unable to (un)register login item: \(error)")
|
||||||
|
}
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
let loginItemId = BundleConfiguration.mainString(for: .loginItemId)
|
||||||
|
appService = SMAppService.loginItem(identifier: loginItemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// AppMenu.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
import AppLibrary
|
||||||
|
import Combine
|
||||||
|
import PassepartoutKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct AppMenu: View {
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
private var profileManager: ProfileManager
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
private var profileProcessor: ProfileProcessor
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
private var tunnel: Tunnel
|
||||||
|
|
||||||
|
@StateObject
|
||||||
|
private var model = Model()
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
versionItem
|
||||||
|
Divider()
|
||||||
|
dockToggle
|
||||||
|
loginToggle
|
||||||
|
Divider()
|
||||||
|
profilesList
|
||||||
|
Divider()
|
||||||
|
quitButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AppMenu {
|
||||||
|
var versionItem: some View {
|
||||||
|
Text(BundleConfiguration.mainVersionString)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dockToggle: some View {
|
||||||
|
Button(model.isVisible ? Strings.Global.hide : Strings.Global.show) {
|
||||||
|
model.isVisible.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginToggle: some View {
|
||||||
|
Toggle(Strings.AppMenu.Items.launchOnLogin, isOn: $model.launchesOnLogin)
|
||||||
|
}
|
||||||
|
|
||||||
|
var profilesList: some View {
|
||||||
|
ForEach(profileManager.headers, id: \.self, content: profileToggle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func profileToggle(for header: ProfileHeader) -> some View {
|
||||||
|
Toggle(header.name, isOn: profileToggleBinding(for: header))
|
||||||
|
}
|
||||||
|
|
||||||
|
func profileToggleBinding(for header: ProfileHeader) -> Binding<Bool> {
|
||||||
|
Binding {
|
||||||
|
header.id == tunnel.currentProfile?.id && tunnel.status != .inactive
|
||||||
|
} set: { isOn in
|
||||||
|
Task {
|
||||||
|
guard let profile = profileManager.profile(withId: header.id) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if isOn {
|
||||||
|
try await tunnel.connect(with: profile, processor: profileProcessor)
|
||||||
|
} else {
|
||||||
|
try await tunnel.disconnect()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
pp_log(.app, .error, "Unable to toggle profile \(header.id) from menu: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var quitButton: some View {
|
||||||
|
Button(Strings.AppMenu.Items.quit(BundleConfiguration.mainDisplayName)) {
|
||||||
|
NSApp.terminate(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// AppMenuImage.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
import PassepartoutKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct AppMenuImage: View, TunnelContextProviding {
|
||||||
|
|
||||||
|
@ObservedObject
|
||||||
|
var connectionObserver: ConnectionObserver
|
||||||
|
|
||||||
|
public init(connectionObserver: ConnectionObserver) {
|
||||||
|
self.connectionObserver = connectionObserver
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
ThemeMenuImage(tunnelConnectionStatus.imageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TunnelStatus {
|
||||||
|
var imageName: Theme.MenuImageName {
|
||||||
|
switch self {
|
||||||
|
case .active:
|
||||||
|
return .active
|
||||||
|
|
||||||
|
case .inactive:
|
||||||
|
return .inactive
|
||||||
|
|
||||||
|
case .activating, .deactivating:
|
||||||
|
return .pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// AppWindow.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public final class AppWindow {
|
||||||
|
public static let shared = AppWindow()
|
||||||
|
|
||||||
|
public var isVisible: Bool {
|
||||||
|
get {
|
||||||
|
NSApp.activationPolicy() == .regular && window.isVisible
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
NSApp.setActivationPolicy(newValue ? .regular : .prohibited)
|
||||||
|
if newValue {
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
window.makeKeyAndOrderFront(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeCloseButton() {
|
||||||
|
window.styleMask.remove(.closable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AppWindow {
|
||||||
|
var window: NSWindow {
|
||||||
|
guard let window = NSApp.windows.first else {
|
||||||
|
fatalError("No Mac window?")
|
||||||
|
}
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -27,15 +27,13 @@ import Foundation
|
||||||
import PassepartoutKit
|
import PassepartoutKit
|
||||||
|
|
||||||
protocol TunnelContextProviding {
|
protocol TunnelContextProviding {
|
||||||
var tunnel: Tunnel { get }
|
|
||||||
|
|
||||||
var connectionObserver: ConnectionObserver { get }
|
var connectionObserver: ConnectionObserver { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
extension TunnelContextProviding {
|
extension TunnelContextProviding {
|
||||||
var tunnelConnectionStatus: TunnelStatus {
|
var tunnelConnectionStatus: TunnelStatus {
|
||||||
var status = tunnel.status
|
var status = connectionObserver.tunnel.status
|
||||||
if status == .active, let connectionStatus = connectionObserver.connectionStatus {
|
if status == .active, let connectionStatus = connectionObserver.connectionStatus {
|
||||||
if connectionStatus == .connected {
|
if connectionStatus == .connected {
|
||||||
status = .active
|
status = .active
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
//
|
||||||
|
// Theme+MenuImageName.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension Theme {
|
||||||
|
public enum MenuImageName {
|
||||||
|
case active
|
||||||
|
case inactive
|
||||||
|
case pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Theme.MenuImageName {
|
||||||
|
static var defaultImageName: (Self) -> String {
|
||||||
|
{
|
||||||
|
switch $0 {
|
||||||
|
case .active: return "MenuActive"
|
||||||
|
case .inactive: return "MenuInactive"
|
||||||
|
case .pending: return "MenuPending"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -434,6 +434,22 @@ struct ThemeImageLabel: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ThemeMenuImage: View {
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
private var theme: Theme
|
||||||
|
|
||||||
|
private let name: Theme.MenuImageName
|
||||||
|
|
||||||
|
init(_ name: Theme.MenuImageName) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Image(theme.menuImageName(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ThemeDisclosableMenu<Content, Label>: View where Content: View, Label: View {
|
struct ThemeDisclosableMenu<Content, Label>: View where Content: View, Label: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
|
@ -80,6 +80,8 @@ public final class Theme: ObservableObject {
|
||||||
|
|
||||||
var systemImageName: (ImageName) -> String = Theme.ImageName.defaultSystemName
|
var systemImageName: (ImageName) -> String = Theme.ImageName.defaultSystemName
|
||||||
|
|
||||||
|
var menuImageName: (MenuImageName) -> String = Theme.MenuImageName.defaultImageName
|
||||||
|
|
||||||
init(dummy: Void) {
|
init(dummy: Void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import SwiftUI
|
||||||
extension TunnelContextProviding where Self: ThemeProviding {
|
extension TunnelContextProviding where Self: ThemeProviding {
|
||||||
var tunnelStatusColor: Color {
|
var tunnelStatusColor: Color {
|
||||||
if connectionObserver.lastErrorCode != nil {
|
if connectionObserver.lastErrorCode != nil {
|
||||||
switch tunnel.status {
|
switch connectionObserver.tunnel.status {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
return theme.inactiveColor
|
return theme.inactiveColor
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,12 @@ import SwiftUI
|
||||||
extension View {
|
extension View {
|
||||||
public func withEnvironment(from context: AppContext, theme: Theme) -> some View {
|
public func withEnvironment(from context: AppContext, theme: Theme) -> some View {
|
||||||
environmentObject(theme)
|
environmentObject(theme)
|
||||||
|
.environmentObject(context.connectionObserver)
|
||||||
.environmentObject(context.iapManager)
|
.environmentObject(context.iapManager)
|
||||||
.environmentObject(context.profileManager)
|
.environmentObject(context.profileManager)
|
||||||
.environmentObject(context.profileProcessor)
|
.environmentObject(context.profileProcessor)
|
||||||
.environmentObject(context.connectionObserver)
|
|
||||||
.environmentObject(context.providerManager)
|
.environmentObject(context.providerManager)
|
||||||
|
.environmentObject(context.tunnel)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withMockEnvironment() -> some View {
|
public func withMockEnvironment() -> some View {
|
||||||
|
|
|
@ -42,6 +42,8 @@ extension BundleConfiguration {
|
||||||
|
|
||||||
case keychainGroupId
|
case keychainGroupId
|
||||||
|
|
||||||
|
case loginItemId
|
||||||
|
|
||||||
case tunnelId
|
case tunnelId
|
||||||
|
|
||||||
// legacy v2
|
// legacy v2
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
//
|
||||||
|
// AppDelegate.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
|
guard !isAppRunning else {
|
||||||
|
NSApp.terminate(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let cfg = NSWorkspace.OpenConfiguration()
|
||||||
|
cfg.hides = true
|
||||||
|
cfg.activates = false
|
||||||
|
cfg.addsToRecentItems = false
|
||||||
|
Task {
|
||||||
|
defer {
|
||||||
|
NSApp.terminate(self)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try await NSWorkspace.shared.openApplication(at: appURL, configuration: cfg)
|
||||||
|
NSLog("Launched main app: \(appURL)")
|
||||||
|
} catch {
|
||||||
|
NSLog("Unable to launch main app: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension AppDelegate {
|
||||||
|
var loginItemId: String {
|
||||||
|
guard let id = Bundle.main.bundleIdentifier else {
|
||||||
|
fatalError("No bundle identifier in LoginItem?")
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
var appId: String {
|
||||||
|
var idComponents = loginItemId.components(separatedBy: ".")
|
||||||
|
idComponents.removeLast()
|
||||||
|
return idComponents.joined(separator: ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
var appURL: URL {
|
||||||
|
let path = Bundle.main.bundlePath as NSString
|
||||||
|
var components = path.pathComponents
|
||||||
|
|
||||||
|
// Passepartout.app/Contents/Library/LoginItems/PassepartoutLoginItem.app
|
||||||
|
components.removeLast(4)
|
||||||
|
|
||||||
|
let appPath = NSString.path(withComponents: components)
|
||||||
|
return URL(fileURLWithPath: appPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAppRunning: Bool {
|
||||||
|
NSWorkspace.shared.runningApplications.contains {
|
||||||
|
$0.bundleIdentifier == appId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Preconcurrency warnings
|
||||||
|
|
||||||
|
extension NSWorkspace: @unchecked Sendable {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSRunningApplication: @unchecked Sendable {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSWorkspace.OpenConfiguration: @unchecked Sendable {
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// PassepartoutLoginItemApp.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 10/29/24.
|
||||||
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/passepartoutvpn
|
||||||
|
//
|
||||||
|
// This file is part of Passepartout.
|
||||||
|
//
|
||||||
|
// Passepartout is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Passepartout is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct PassepartoutLoginItemApp: App {
|
||||||
|
|
||||||
|
@NSApplicationDelegateAdaptor
|
||||||
|
private var appDelegate: AppDelegate
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
MenuBarExtra("") {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue