mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 04:58:56 +00:00
Move to AF swift package
This commit is contained in:
@@ -8,8 +8,6 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; };
|
||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A312B068AD700376976 /* NewWin.swift */; };
|
||||
C7045A362B068DD700376976 /* PermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A352B068DD700376976 /* PermView.swift */; };
|
||||
C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71848432B8D2D600046CB13 /* ZombieView.swift */; };
|
||||
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
|
||||
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
|
||||
@@ -22,7 +20,6 @@
|
||||
C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; };
|
||||
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* RegularMode.swift */; };
|
||||
C76EDCD62C45DC82000CF29D /* AlinFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = C76EDCD52C45DC82000CF29D /* AlinFoundation */; };
|
||||
C771CB762BD028AF00C33A18 /* SegmentedPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */; };
|
||||
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; };
|
||||
C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C77B90072AF18E2F009CC655 /* Assets.xcassets */; };
|
||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90152AF19377009CC655 /* AppState.swift */; };
|
||||
@@ -36,20 +33,15 @@
|
||||
C79947962C34A794007279CC /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79947952C34A794007279CC /* Tips.swift */; };
|
||||
C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */ = {isa = PBXBuildFile; fileRef = C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */; };
|
||||
C7A2AE5D2B9644F300161540 /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7A2AE5C2B9644F300161540 /* FileWatcher */; };
|
||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A9CE462B89164700EB6E78 /* Authorization.swift */; };
|
||||
C7ACADE62B92A737000B5845 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ACADE52B92A737000B5845 /* Features.swift */; };
|
||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CF47232B3B3F1700979C5F /* WindowSettings.swift */; };
|
||||
C7D15BBE2BE85F4D007D57CF /* FeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */; };
|
||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
||||
C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */ = {isa = PBXBuildFile; fileRef = C7DC1FED2B9F7D3D009AC317 /* features.json */; };
|
||||
C7DD49EA2BAB4D8100CCBA16 /* AppInfoFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */; };
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */; };
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; };
|
||||
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
||||
C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */; };
|
||||
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; };
|
||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; };
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
|
||||
C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FEBA102BDC422200AE195F /* AppSearchView.swift */; };
|
||||
@@ -100,8 +92,6 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = "<group>"; };
|
||||
C7045A312B068AD700376976 /* NewWin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewWin.swift; sourceTree = "<group>"; };
|
||||
C7045A352B068DD700376976 /* PermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermView.swift; sourceTree = "<group>"; };
|
||||
C71848432B8D2D600046CB13 /* ZombieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZombieView.swift; sourceTree = "<group>"; };
|
||||
C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -113,7 +103,6 @@
|
||||
C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = "<group>"; };
|
||||
C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
|
||||
C76D08542AF89CDE00D07867 /* RegularMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegularMode.swift; sourceTree = "<group>"; };
|
||||
C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPicker.swift; sourceTree = "<group>"; };
|
||||
C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearcleanerApp.swift; sourceTree = "<group>"; };
|
||||
C77B90072AF18E2F009CC655 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -133,20 +122,15 @@
|
||||
C79947952C34A794007279CC /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = "<group>"; };
|
||||
C7A27E7F2AFD70DD00166168 /* PearcleanerSentinel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PearcleanerSentinel.entitlements; sourceTree = "<group>"; };
|
||||
C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.alienator88.PearcleanerSentinel.plist; sourceTree = "<group>"; };
|
||||
C7A9CE462B89164700EB6E78 /* Authorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = "<group>"; };
|
||||
C7ACADE52B92A737000B5845 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
|
||||
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSettings.swift; sourceTree = "<group>"; };
|
||||
C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureView.swift; sourceTree = "<group>"; };
|
||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
|
||||
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
|
||||
C7DC1FED2B9F7D3D009AC317 /* features.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = features.json; sourceTree = SOURCE_ROOT; };
|
||||
C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoFetch.swift; sourceTree = "<group>"; };
|
||||
C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversePathsFetch.swift; sourceTree = "<group>"; };
|
||||
C7DD49EF2BABA14400CCBA16 /* Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folders.swift; sourceTree = "<group>"; };
|
||||
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = "<group>"; };
|
||||
C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionBuilderView.swift; sourceTree = "<group>"; };
|
||||
C7ED86B02BA8A35500F13DE4 /* Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = "<group>"; };
|
||||
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trash.swift; sourceTree = "<group>"; };
|
||||
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = "<group>"; };
|
||||
C7FEBA102BDC422200AE195F /* AppSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSearchView.swift; sourceTree = "<group>"; };
|
||||
@@ -179,18 +163,6 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
C7045A302B068ABE00376976 /* Windows */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C7045A312B068AD700376976 /* NewWin.swift */,
|
||||
C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */,
|
||||
C7045A352B068DD700376976 /* PermView.swift */,
|
||||
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */,
|
||||
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */,
|
||||
);
|
||||
path = Windows;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C72893072AFD442600C8C1CD /* Logic */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -206,9 +178,6 @@
|
||||
C77B901B2AF193A3009CC655 /* Styles.swift */,
|
||||
C7D31D502AFF00F300C7ED9E /* Locations.swift */,
|
||||
C74FDC622BCD833D00B8960F /* Conditions.swift */,
|
||||
C7A9CE462B89164700EB6E78 /* Authorization.swift */,
|
||||
C7ACADE52B92A737000B5845 /* Features.swift */,
|
||||
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */,
|
||||
);
|
||||
path = Logic;
|
||||
sourceTree = "<group>";
|
||||
@@ -246,7 +215,6 @@
|
||||
C76D084F2AF84E0100D07867 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C7DC1FED2B9F7D3D009AC317 /* features.json */,
|
||||
C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */,
|
||||
C77B90142AF18E7C009CC655 /* Info.plist */,
|
||||
C77B90072AF18E2F009CC655 /* Assets.xcassets */,
|
||||
@@ -255,14 +223,6 @@
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C771CB742BD028A000C33A18 /* Picker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */,
|
||||
);
|
||||
path = Picker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C77B8FF72AF18E2E009CC655 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -292,7 +252,6 @@
|
||||
C76D084F2AF84E0100D07867 /* Resources */,
|
||||
C76D08462AF83C2F00D07867 /* Settings */,
|
||||
C77B901F2AF1B390009CC655 /* Views */,
|
||||
C7045A302B068ABE00376976 /* Windows */,
|
||||
C72893072AFD442600C8C1CD /* Logic */,
|
||||
);
|
||||
path = Pearcleaner;
|
||||
@@ -301,7 +260,6 @@
|
||||
C77B901F2AF1B390009CC655 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C771CB742BD028A000C33A18 /* Picker */,
|
||||
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
||||
C71848432B8D2D600046CB13 /* ZombieView.swift */,
|
||||
C76D08542AF89CDE00D07867 /* RegularMode.swift */,
|
||||
@@ -310,6 +268,8 @@
|
||||
C7FEBA102BDC422200AE195F /* AppSearchView.swift */,
|
||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||
C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */,
|
||||
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */,
|
||||
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -439,7 +399,6 @@
|
||||
files = (
|
||||
C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */,
|
||||
C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */,
|
||||
C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -471,10 +430,8 @@
|
||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */,
|
||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */,
|
||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||
C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */,
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */,
|
||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */,
|
||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
||||
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
||||
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
||||
@@ -486,19 +443,14 @@
|
||||
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */,
|
||||
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
||||
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
|
||||
C7045A362B068DD700376976 /* PermView.swift in Sources */,
|
||||
C76D08512AF850F300D07867 /* About.swift in Sources */,
|
||||
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */,
|
||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */,
|
||||
C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */,
|
||||
C771CB762BD028AF00C33A18 /* SegmentedPicker.swift in Sources */,
|
||||
C74412C72BBF249000DDFCA8 /* AppPathsFetch.swift in Sources */,
|
||||
C74FDC632BCD833D00B8960F /* Conditions.swift in Sources */,
|
||||
C7ACADE62B92A737000B5845 /* Features.swift in Sources */,
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */,
|
||||
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */,
|
||||
C7D15BBE2BE85F4D007D57CF /* FeatureView.swift in Sources */,
|
||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
|
||||
C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */,
|
||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */,
|
||||
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */,
|
||||
@@ -699,6 +651,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -714,6 +667,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
@@ -753,6 +707,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
||||
@@ -18,18 +18,12 @@ class AppState: ObservableObject {
|
||||
@Published var zombieFile: ZombieFile
|
||||
@Published var sortedApps: [AppInfo] = []
|
||||
@Published var selectedItems = Set<URL>()
|
||||
@Published var alertType = AlertType.off
|
||||
@Published var currentView = CurrentDetailsView.empty
|
||||
@Published var showAlert: Bool = false
|
||||
@Published var sidebar: Bool = true
|
||||
@Published var progressBar: (String, Double) = ("Ready", 0.0)
|
||||
@Published var reload: Bool = false
|
||||
@Published var showProgress: Bool = false
|
||||
@Published var finderExtensionEnabled: Bool = false
|
||||
// @Published var updateAvailable: Bool = false
|
||||
@Published var featureAvailable: Bool = false
|
||||
// @Published var permissionsOkay: Bool = true
|
||||
// @Published var permissionResults: PermissionsCheckResults?
|
||||
@Published var showUninstallAlert: Bool = false
|
||||
@Published var oneShotMode: Bool = false
|
||||
@Published var showConditionBuilder: Bool = false
|
||||
@@ -178,55 +172,3 @@ enum CurrentDetailsView:Int
|
||||
case apps
|
||||
case zombie
|
||||
}
|
||||
|
||||
enum NewWindow:Int
|
||||
{
|
||||
case perm
|
||||
case feature
|
||||
}
|
||||
|
||||
|
||||
enum AlertType:Int
|
||||
{
|
||||
case diskAccess
|
||||
case update
|
||||
case no_update
|
||||
case restartApp
|
||||
case off
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum DisplayMode: Int, CaseIterable {
|
||||
case system, dark, light
|
||||
|
||||
var colorScheme: ColorScheme? {
|
||||
get {
|
||||
switch self {
|
||||
case .system: return nil
|
||||
case .dark: return ColorScheme.dark
|
||||
case .light: return ColorScheme.light
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch newValue {
|
||||
case .none:
|
||||
self = .system
|
||||
case .dark?:
|
||||
self = .dark
|
||||
case .light?:
|
||||
self = .light
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .system: return "System"
|
||||
case .dark: return "Dark"
|
||||
case .light: return "Light"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
//
|
||||
// Authorization.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 2/23/24.
|
||||
//
|
||||
|
||||
//import Foundation
|
||||
//import Swift
|
||||
//
|
||||
//// https://github.com/x13a/authorization-swift
|
||||
//
|
||||
//public struct Authorization {
|
||||
//
|
||||
// public enum Error: Swift.Error {
|
||||
// case create(OSStatus)
|
||||
// case copyRights(OSStatus)
|
||||
// case exec(OSStatus)
|
||||
// }
|
||||
//
|
||||
// public static func executeWithPrivileges(
|
||||
// _ command: String
|
||||
// ) -> Result<FileHandle, Error> {
|
||||
//
|
||||
// let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
|
||||
// var fn: @convention(c) (
|
||||
// AuthorizationRef,
|
||||
// UnsafePointer<CChar>, // path
|
||||
// AuthorizationFlags,
|
||||
// UnsafePointer<UnsafePointer<CChar>?>, // args
|
||||
// UnsafeMutablePointer<UnsafeMutablePointer<FILE>>?
|
||||
// ) -> OSStatus
|
||||
// fn = unsafeBitCast(
|
||||
// dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges"),
|
||||
// to: type(of: fn)
|
||||
// )
|
||||
//
|
||||
// var authorizationRef: AuthorizationRef? = nil
|
||||
// var err = AuthorizationCreate(nil, nil, [], &authorizationRef)
|
||||
// guard err == errAuthorizationSuccess else {
|
||||
// return .failure(.create(err))
|
||||
// }
|
||||
// defer { AuthorizationFree(authorizationRef!, [.destroyRights]) }
|
||||
//
|
||||
// var components = command.components(separatedBy: " ")
|
||||
// var path = components.remove(at: 0).cString(using: .utf8)!
|
||||
// let name = kAuthorizationRightExecute.cString(using: .utf8)!
|
||||
//
|
||||
// var items: AuthorizationItem = name.withUnsafeBufferPointer { nameBuf in
|
||||
// path.withUnsafeBufferPointer { pathBuf in
|
||||
// let pathPtr =
|
||||
// UnsafeMutableRawPointer(mutating: pathBuf.baseAddress!)
|
||||
// return AuthorizationItem(
|
||||
// name: nameBuf.baseAddress!,
|
||||
// valueLength: path.count,
|
||||
// value: pathPtr,
|
||||
// flags: 0
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var rights: AuthorizationRights =
|
||||
// withUnsafeMutablePointer(to: &items) { items in
|
||||
// return AuthorizationRights(count: 1, items: items)
|
||||
// }
|
||||
//
|
||||
// let flags: AuthorizationFlags = [
|
||||
// .interactionAllowed,
|
||||
// .preAuthorize,
|
||||
// .extendRights,
|
||||
// ]
|
||||
//
|
||||
// err = AuthorizationCopyRights(
|
||||
// authorizationRef!,
|
||||
// &rights,
|
||||
// nil,
|
||||
// flags,
|
||||
// nil
|
||||
// )
|
||||
// guard err == errAuthorizationSuccess else {
|
||||
// return .failure(.copyRights(err))
|
||||
// }
|
||||
//
|
||||
// let rest = components.map { $0.cString(using: .utf8)! }
|
||||
// var args = Array<UnsafePointer<CChar>?>(
|
||||
// repeating: nil,
|
||||
// count: rest.count + 1
|
||||
// )
|
||||
// for (idx, arg) in rest.enumerated() {
|
||||
// args[idx] = UnsafePointer<CChar>?(arg)
|
||||
// }
|
||||
//
|
||||
// var file = FILE()
|
||||
// let fh: FileHandle?
|
||||
//
|
||||
// (err, fh) = withUnsafeMutablePointer(to: &file) { file in
|
||||
// var pipe = file
|
||||
// let err = fn(authorizationRef!, &path, [], &args, &pipe)
|
||||
// guard err == errAuthorizationSuccess else {
|
||||
// return (err, nil)
|
||||
// }
|
||||
// let fh = FileHandle(
|
||||
// fileDescriptor: fileno(pipe),
|
||||
// closeOnDealloc: true
|
||||
// )
|
||||
// return (err, fh)
|
||||
// }
|
||||
// guard err == errAuthorizationSuccess else {
|
||||
// return .failure(.exec(err))
|
||||
// }
|
||||
// return .success(fh!)
|
||||
// }
|
||||
//}
|
||||
@@ -1,113 +0,0 @@
|
||||
//
|
||||
// Features.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 3/1/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import AlinFoundation
|
||||
|
||||
|
||||
func getFeatures(appState: AppState, features: Binding<String>) {
|
||||
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/features.json")!
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept")
|
||||
request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version")
|
||||
|
||||
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let data = data {
|
||||
do {
|
||||
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
|
||||
if let jsonDict = jsonObject as? [String: String],
|
||||
let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
|
||||
let featureText = jsonDict[bundleVersion] {
|
||||
if features.wrappedValue != featureText.featureFormat() {
|
||||
updateOnMain {
|
||||
features.wrappedValue = featureText.featureFormat()
|
||||
appState.featureAvailable = true
|
||||
// show.wrappedValue = true
|
||||
}
|
||||
} else {
|
||||
updateOnMain {
|
||||
appState.featureAvailable = false
|
||||
}
|
||||
// show.wrappedValue = false
|
||||
// print("Same version")
|
||||
}
|
||||
|
||||
} else {
|
||||
updateOnMain {
|
||||
appState.featureAvailable = false
|
||||
}
|
||||
// show.wrappedValue = false
|
||||
// print("No features for version found")
|
||||
}
|
||||
} catch {
|
||||
printOS("Error reading features JSON from GitHub: \(error.localizedDescription)")
|
||||
}
|
||||
} else {
|
||||
printOS("Error reading features JSON from GitHub: \(error?.localizedDescription ?? "Unknown error")")
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct FeatureNotificationView: View {
|
||||
let appState: AppState
|
||||
@State private var hovered: Bool = false
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
||||
Text("New Features!")
|
||||
.font(.callout)
|
||||
.opacity(0.5)
|
||||
.padding(.leading, 7)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: !hovered ? "star" : "star.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 14, height: 14)
|
||||
.animation(.easeInOut(duration: 0.2), value: hovered)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
|
||||
Text("Check")
|
||||
.foregroundStyle(.white)
|
||||
|
||||
}
|
||||
.padding(3)
|
||||
.onHover { hovering in
|
||||
withAnimation() {
|
||||
hovered = hovering
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
NewWin.show(appState: appState, width: 500, height: 400, newWin: .feature)
|
||||
updateOnMain {
|
||||
appState.featureAvailable = false
|
||||
}
|
||||
}
|
||||
.help("View latest features")
|
||||
.padding(.horizontal, 5)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color("pear"))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||
|
||||
}
|
||||
.frame(height: 30)
|
||||
.padding(5)
|
||||
.background(.primary.opacity(0.05))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
@@ -146,59 +146,6 @@ struct RescanButton: ButtonStyle {
|
||||
|
||||
|
||||
|
||||
public struct NewFeatureView: View {
|
||||
var text: String
|
||||
var mini: Bool
|
||||
@Binding var showFeature: Bool
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "star")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20)
|
||||
Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!")
|
||||
.font(.headline).bold()
|
||||
Spacer()
|
||||
Button("") {
|
||||
withAnimation(Animation.easeInOut(duration: 0.5)) {
|
||||
showFeature = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: (.primary.opacity(0.5))))
|
||||
.onHover { isHovered in
|
||||
if isHovered {
|
||||
NSCursor.pointingHand.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
ScrollView() {
|
||||
Text(text)
|
||||
.font(.callout)
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(.trailing)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom)
|
||||
|
||||
}
|
||||
.frame(maxWidth: mini ? 200 : 400, maxHeight: mini ? 300 : 250)
|
||||
.background(.ultraThinMaterial)
|
||||
.cornerRadius(10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(.primary.opacity(0.2), lineWidth: 0.6)
|
||||
)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct SlideableDivider: View {
|
||||
@EnvironmentObject private var themeManager: ThemeManager
|
||||
@Binding var dimension: Double
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
//
|
||||
// Trash.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 3/18/24.
|
||||
//
|
||||
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
//class TrashManager {
|
||||
// private var fileLocations: [URL: URL] = [:] // Maps original location to trash location
|
||||
//
|
||||
// // Function to move files to Trash and track their original locations
|
||||
// func delete(fileURLs: [URL]) {
|
||||
// let fileManager = FileManager.default
|
||||
// let trashURL = try! fileManager.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
|
||||
//
|
||||
// for originalURL in fileURLs {
|
||||
// let destinationURL = trashURL.appendingPathComponent(originalURL.lastPathComponent)
|
||||
//
|
||||
// do {
|
||||
// // The resultingItemURL is the new location of the item in the trash.
|
||||
// var resultingItemURL: NSURL?
|
||||
// try fileManager.trashItem(at: originalURL, resultingItemURL: &resultingItemURL)
|
||||
// if let trashPath = resultingItemURL as URL? {
|
||||
// // Track the original URL and its corresponding location in the Trash
|
||||
// fileLocations[originalURL] = trashPath
|
||||
// }
|
||||
// } catch {
|
||||
// print("Error moving file to Trash: \(error)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Function to undo the deletion of files, moving them back from Trash to their original locations
|
||||
// func undoDelete() {
|
||||
// let fileManager = FileManager.default
|
||||
//
|
||||
// for (originalURL, trashURL) in fileLocations {
|
||||
// do {
|
||||
// // Attempt to move the item back to its original location
|
||||
// try fileManager.moveItem(at: trashURL, to: originalURL)
|
||||
// } catch {
|
||||
// print("Error moving file back from Trash: \(error)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Clear the tracking dictionary after undoing the deletions
|
||||
// fileLocations.removeAll()
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//import Foundation
|
||||
//
|
||||
//func moveToTrash(fileURLs: [URL], completion: @escaping () -> Void = {}) {
|
||||
// for fileURL in fileURLs {
|
||||
// do {
|
||||
// try _ = fileURL.checkResourceIsReachable()
|
||||
// } catch {
|
||||
// printOS(error.localizedDescription)
|
||||
// }
|
||||
// }
|
||||
// let target: NSAppleEventDescriptor = .init(bundleIdentifier: "com.apple.finder")
|
||||
// let event: NSAppleEventDescriptor = .init(eventClass: kAECoreSuite,
|
||||
// eventID: AEEventID(kAEDelete),
|
||||
// targetDescriptor: target,
|
||||
// returnID: AEReturnID(kAutoGenerateReturnID),
|
||||
// transactionID: AETransactionID(kAnyTransactionID))
|
||||
// let fileList: NSAppleEventDescriptor = fileURLs.enumerated().reduce(into: .init(listDescriptor: ())) {
|
||||
// (result: inout NSAppleEventDescriptor, element: (offset: Int, element: URL)) in
|
||||
// if let nativePath: NSAppleEventDescriptor = .init(
|
||||
// descriptorType: typeFileURL,
|
||||
// data: element.element.absoluteString.data(using: .utf8)
|
||||
// ) {
|
||||
// result.insert(nativePath, at: element.offset + 1)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// event.setParam(fileList, forKeyword: keyDirectObject)
|
||||
//
|
||||
// do {
|
||||
// try event.sendEvent(options: .noReply, timeout: TimeInterval(kAEDefaultTimeout))
|
||||
// } catch let error as NSError {
|
||||
// if case -600 = error.code {
|
||||
// printOS("Finder is not running.")
|
||||
// } else {
|
||||
// printOS(error.description)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// completion()
|
||||
//}
|
||||
|
||||
@@ -10,27 +10,6 @@ import SwiftUI
|
||||
import AlinFoundation
|
||||
import AppKit
|
||||
|
||||
// Make updates on main thread
|
||||
//func updateOnMain(after delay: Double? = nil, _ updates: @escaping () -> Void) {
|
||||
// if let delay = delay {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
|
||||
// updates()
|
||||
// }
|
||||
// } else {
|
||||
// DispatchQueue.main.async {
|
||||
// updates()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//// Execute functions on background thread
|
||||
//func updateOnBackground(_ updates: @escaping () -> Void) {
|
||||
// DispatchQueue.global(qos: .userInitiated).async {
|
||||
// updates()
|
||||
// }
|
||||
//}
|
||||
|
||||
// Reload apps list
|
||||
func reloadAppsList(appState: AppState, fsm: FolderSettingsManager) {
|
||||
appState.reload = true
|
||||
@@ -54,60 +33,6 @@ func resizeWindowAuto(windowSettings: WindowSettings, title: String) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check app directory based on user permission
|
||||
//func checkAppDirectoryAndUserRole(completion: @escaping ((isInCorrectDirectory: Bool, isAdmin: Bool)) -> Void) {
|
||||
// isCurrentUserAdmin { isAdmin in
|
||||
// let bundlePath = Bundle.main.bundlePath as NSString
|
||||
// let applicationsDir = "/Applications"
|
||||
// let userApplicationsDir = "\(home)/Applications"
|
||||
//
|
||||
// var isInCorrectDirectory = false
|
||||
//
|
||||
// if isAdmin {
|
||||
// // Admins can have the app in either /Applications or ~/Applications
|
||||
// isInCorrectDirectory = bundlePath.deletingLastPathComponent == applicationsDir ||
|
||||
// bundlePath.deletingLastPathComponent == userApplicationsDir
|
||||
// } else {
|
||||
// // Standard users should only have the app in ~/Applications
|
||||
// isInCorrectDirectory = bundlePath.deletingLastPathComponent == userApplicationsDir
|
||||
// }
|
||||
//
|
||||
// // Return both conditions: if the app is in the correct directory and if the user is an admin
|
||||
// completion((isInCorrectDirectory, isAdmin))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//// Check if user is admin or standard user
|
||||
//func isCurrentUserAdmin(completion: @escaping (Bool) -> Void) {
|
||||
// let process = Process()
|
||||
// process.executableURL = URL(fileURLWithPath: "/bin/zsh") // Using zsh, macOS default shell
|
||||
// process.arguments = ["-c", "groups $(whoami) | grep -q ' admin '"]
|
||||
//
|
||||
// process.terminationHandler = { process in
|
||||
// // On macOS, a process's exit status of 0 indicates success (admin group found in this context)
|
||||
// completion(process.terminationStatus == 0)
|
||||
// }
|
||||
//
|
||||
// do {
|
||||
// try process.run()
|
||||
// } catch {
|
||||
// print("Failed to execute command: \(error)")
|
||||
// completion(false)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Check if appearance is dark mode
|
||||
//func isDarkMode() -> Bool {
|
||||
// return NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
|
||||
//}
|
||||
//
|
||||
//// Set app color mode
|
||||
//func setAppearance(mode: NSAppearance.Name) {
|
||||
// NSApp.appearance = NSAppearance(named: mode)
|
||||
//}
|
||||
|
||||
// Check if Pearcleaner has any windows open
|
||||
func hasWindowOpen() -> Bool {
|
||||
for window in NSApp.windows where window.title == "Pearcleaner" {
|
||||
@@ -116,14 +41,7 @@ func hasWindowOpen() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find and hide/show main app window when using menubar item
|
||||
//func findAndHideWindows(named titles: [String]) {
|
||||
// for title in titles {
|
||||
// if let window = NSApp.windows.first(where: { $0.title == title }) {
|
||||
// window.close()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSettings) {
|
||||
windowSettings.registerDefaultWindowSettings() {
|
||||
@@ -138,22 +56,6 @@ func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSetting
|
||||
}
|
||||
|
||||
|
||||
//func findAndShowWindows(named titles: [String]) {
|
||||
// for title in titles {
|
||||
// if let window = NSApp.windows.first(where: { $0.title == title }) {
|
||||
// window.makeKeyAndOrderFront(nil)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Copy to clipboard
|
||||
//func copyToClipboard(text: String) {
|
||||
// let pasteboard = NSPasteboard.general
|
||||
// pasteboard.clearContents()
|
||||
// pasteboard.setString(text, forType: .string)
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
// Brew cleanup
|
||||
@@ -222,17 +124,6 @@ func saveURLsToFile(urls: Set<URL>, appState: AppState) {
|
||||
}
|
||||
|
||||
|
||||
// Check if symlink
|
||||
//func isSymlink(atPath path: URL) -> Bool {
|
||||
// do {
|
||||
// let _ = try path.checkResourceIsReachable()
|
||||
// let resourceValues = try path.resourceValues(forKeys: [.isSymbolicLinkKey])
|
||||
// return resourceValues.isSymbolicLink == true
|
||||
// } catch {
|
||||
// return false
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// Open trash folder
|
||||
func openTrash() {
|
||||
@@ -295,64 +186,6 @@ func checkAppBundleArchitecture(at appBundlePath: String) -> Arch {
|
||||
|
||||
|
||||
|
||||
// Convert icon to png so colors render correctly
|
||||
//func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? {
|
||||
// // Resize the icon to the specified size
|
||||
// let resizedIcon = NSImage(size: size)
|
||||
// resizedIcon.lockFocus()
|
||||
// icon.draw(in: NSRect(x: 0, y: 0, width: size.width, height: size.height))
|
||||
// resizedIcon.unlockFocus()
|
||||
//
|
||||
// // Convert the resized icon to PNG format
|
||||
// if let resizedImageData = resizedIcon.tiffRepresentation,
|
||||
// let resizedBitmap = NSBitmapImageRep(data: resizedImageData),
|
||||
// let pngData = resizedBitmap.representation(using: .png, properties: [:]) {
|
||||
// return NSImage(data: pngData)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
|
||||
// Get icon for files and folders
|
||||
//func getIconForFileOrFolder(atPath path: URL) -> Image? {
|
||||
// return Image(nsImage: NSWorkspace.shared.icon(forFile: path.path))
|
||||
//}
|
||||
//
|
||||
//func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? {
|
||||
// return NSWorkspace.shared.icon(forFile: path.path)
|
||||
//}
|
||||
|
||||
|
||||
// Get average color from image
|
||||
//extension NSImage {
|
||||
// var averageColor: NSColor? {
|
||||
// guard let tiffData = self.tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffData), let inputImage = CIImage(bitmapImageRep: bitmapImage) else { return nil }
|
||||
//
|
||||
// let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height)
|
||||
//
|
||||
// guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil }
|
||||
// guard let outputImage = filter.outputImage else { return nil }
|
||||
//
|
||||
// var bitmap = [UInt8](repeating: 0, count: 4)
|
||||
// let context = CIContext(options: [.workingColorSpace: NSNull()])
|
||||
// context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: CIFormat.RGBA8, colorSpace: nil)
|
||||
//
|
||||
// return NSColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// Relaunch app
|
||||
//func relaunchApp(afterDelay seconds: TimeInterval = 0.5) -> Never {
|
||||
// let task = Process()
|
||||
// task.launchPath = "/bin/sh"
|
||||
// task.arguments = ["-c", "sleep \(seconds); open \"\(Bundle.main.bundlePath)\""]
|
||||
// task.launch()
|
||||
//
|
||||
// NSApp.terminate(nil)
|
||||
// exit(0)
|
||||
//}
|
||||
|
||||
// Check if app is running before deleting app files
|
||||
func killApp(appId: String, completion: @escaping () -> Void = {}) {
|
||||
let runningApps = NSWorkspace.shared.runningApplications
|
||||
@@ -459,23 +292,6 @@ func isNested(path: URL) -> Bool {
|
||||
return parentDirectory != applicationsPath && parentDirectory != homeApplicationsPath
|
||||
}
|
||||
|
||||
// Check if macOS is 14.0 or higher
|
||||
//func isMacOS14OrHigher() -> Bool {
|
||||
// if #available(macOS 14, *) {
|
||||
// return true
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// --- Extend Int to convert hours to seconds ---
|
||||
//extension Int {
|
||||
// var daysToSeconds: Double {
|
||||
// return Double(self) * 24 * 60 * 60
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// --- Extend String to remove periods, spaces and lowercase the string
|
||||
@@ -486,23 +302,6 @@ extension String {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Extend string to replace - and | with custom characters
|
||||
extension String {
|
||||
func featureFormat() -> String {
|
||||
return self.replacingOccurrences(of: "- ", with: "• ").replacingOccurrences(of: "|", with: "\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
// --- Capitalize first letter of string only
|
||||
//extension String {
|
||||
// func capitalizingFirstLetter() -> String {
|
||||
// return prefix(1).capitalized + dropFirst()
|
||||
// }
|
||||
//
|
||||
// mutating func capitalizeFirstLetter() {
|
||||
// self = self.capitalizingFirstLetter()
|
||||
// }
|
||||
//}
|
||||
|
||||
// --- Returns comma separated string as array of strings
|
||||
extension String {
|
||||
@@ -515,130 +314,6 @@ extension String {
|
||||
}
|
||||
|
||||
|
||||
// --- Overload the greater than operator ">" to do a semantic check on the string versions
|
||||
//extension String {
|
||||
// func versionStringToTuple() -> (Int, Int, Int) {
|
||||
// let components = self.split(separator: ".").compactMap { Int($0) }
|
||||
// return (components[0], components[1], components[2])
|
||||
// }
|
||||
//
|
||||
// static func > (lhs: String, rhs: String) -> Bool {
|
||||
// let lhsVersion = lhs.versionStringToTuple()
|
||||
// let rhsVersion = rhs.versionStringToTuple()
|
||||
// return lhsVersion > rhsVersion
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// --- Trash Relationship ---
|
||||
//extension FileManager {
|
||||
// public func isInTrash(_ file: URL) -> Bool {
|
||||
// var relationship: URLRelationship = .other
|
||||
// try? getRelationship(&relationship, of: .trashDirectory, in: .userDomainMask, toItemAt: file)
|
||||
// return relationship == .contains
|
||||
// }
|
||||
//}
|
||||
|
||||
// --- Extend print command to also output to the Console ---
|
||||
//func printOS(_ items: Any..., separator: String = " ", terminator: String = "\n") {
|
||||
// let message = items.map { "\($0)" }.joined(separator: separator)
|
||||
// let log = OSLog(subsystem: "pearcleaner", category: "Application")
|
||||
// os_log("%@", log: log, type: .default, message)
|
||||
//}
|
||||
|
||||
|
||||
// Get size of files
|
||||
//func totalSizeOnDisk(for paths: [URL]) -> (real: Int64, logical: Int64) {
|
||||
// let fileManager = FileManager.default
|
||||
// var totalAllocatedSize: Int64 = 0
|
||||
// var totalFileSize: Int64 = 0
|
||||
//
|
||||
// for url in paths {
|
||||
// var isDirectory: ObjCBool = false
|
||||
// if fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) {
|
||||
// let keys: [URLResourceKey] = [.totalFileAllocatedSizeKey, .fileSizeKey]
|
||||
// if isDirectory.boolValue {
|
||||
// // It's a directory, recurse into it
|
||||
// if let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: keys, errorHandler: nil) {
|
||||
// for case let fileURL as URL in enumerator {
|
||||
// do {
|
||||
// let fileAttributes = try fileURL.resourceValues(forKeys: Set(keys))
|
||||
// if let allocatedSize = fileAttributes.totalFileAllocatedSize {
|
||||
// totalAllocatedSize += Int64(allocatedSize)
|
||||
// }
|
||||
// if let fileSize = fileAttributes.fileSize {
|
||||
// totalFileSize += Int64(fileSize)
|
||||
// }
|
||||
// } catch {
|
||||
// print("Error getting file attributes for \(fileURL): \(error)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // It's a file
|
||||
// do {
|
||||
// let fileAttributes = try url.resourceValues(forKeys: Set(keys))
|
||||
// if let allocatedSize = fileAttributes.totalFileAllocatedSize {
|
||||
// totalAllocatedSize += Int64(allocatedSize)
|
||||
// }
|
||||
// if let fileSize = fileAttributes.fileSize {
|
||||
// totalFileSize += Int64(fileSize)
|
||||
// }
|
||||
// } catch {
|
||||
// print("Error getting file attributes for \(url): \(error)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return (real: totalAllocatedSize, logical: totalFileSize)
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
//func totalSizeOnDisk(for path: URL) -> (real: Int64, logical: Int64) {
|
||||
// return totalSizeOnDisk(for: [path])
|
||||
//}
|
||||
//
|
||||
//// ByteFormatter
|
||||
//func formatByte(size: Int64) -> (human: String, byte: String) {
|
||||
// let byteCountFormatter = ByteCountFormatter()
|
||||
// byteCountFormatter.countStyle = .file
|
||||
// byteCountFormatter.allowedUnits = [.useAll]
|
||||
// let human = byteCountFormatter.string(fromByteCount: size)
|
||||
//
|
||||
// let numberformatter = NumberFormatter()
|
||||
// numberformatter.numberStyle = .decimal
|
||||
// let formattedNumber = numberformatter.string(from: NSNumber(value: size)) ?? "\(size)"
|
||||
// let byte = "\(formattedNumber)"
|
||||
//
|
||||
// return (human: human, byte: byte)
|
||||
//
|
||||
//}
|
||||
|
||||
|
||||
// Only process supported files
|
||||
//func isSupportedFileType(at path: String) -> Bool {
|
||||
// let fileManager = FileManager.default
|
||||
// do {
|
||||
// let attributes = try fileManager.attributesOfItem(atPath: path)
|
||||
// if let fileType = attributes[FileAttributeKey.type] as? FileAttributeType {
|
||||
// switch fileType {
|
||||
// case .typeRegular, .typeDirectory, .typeSymbolicLink:
|
||||
// // The file is a regular file, directory, or symbolic link
|
||||
// return true
|
||||
// default:
|
||||
// // The file is a socket, pipe, or another type not supported
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// } catch {
|
||||
// printOS("Error getting file attributes: \(error)")
|
||||
// }
|
||||
// return false
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
// --- Pearcleaner Uninstall --
|
||||
@@ -668,47 +343,6 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Create Application Support folder if it doesn't exist ---
|
||||
//func ensureApplicationSupportFolderExists(appState: AppState) {
|
||||
// let fileManager = FileManager.default
|
||||
// let supportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("com.alienator88.Pearcleaner")
|
||||
//
|
||||
// // Check to make sure Application Support/Pearcleaner folder exists
|
||||
// if !fileManager.fileExists(atPath: supportURL.path) {
|
||||
// try! fileManager.createDirectory(at: supportURL, withIntermediateDirectories: true)
|
||||
// printOS("Created Application Support/com.alienator88.Pearcleaner folder")
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// --- Write Log to File for troubleshooting ---
|
||||
//func writeLog(string: String) {
|
||||
// let fileManager = FileManager.default
|
||||
// let home = fileManager.homeDirectoryForCurrentUser.path
|
||||
// let logFilePath = "\(home)/Downloads/log.txt"
|
||||
//
|
||||
// // Check if the log file exists, and create it if it doesn't
|
||||
// if !fileManager.fileExists(atPath: logFilePath) {
|
||||
// if !fileManager.createFile(atPath: logFilePath, contents: nil, attributes: nil) {
|
||||
// printOS("Failed to create the log file.")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// do {
|
||||
// if let fileHandle = FileHandle(forWritingAtPath: logFilePath) {
|
||||
// let ns = "\(string)\n"
|
||||
// fileHandle.seekToEndOfFile()
|
||||
// fileHandle.write(ns.data(using: .utf8)!)
|
||||
// fileHandle.closeFile()
|
||||
// } else {
|
||||
// printOS("Error opening file for appending")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// --- Load Plist file with launchctl ---
|
||||
func launchctl(load: Bool, completion: @escaping () -> Void = {}) {
|
||||
let cmd = load ? "load" : "unload"
|
||||
@@ -749,22 +383,3 @@ func sendStartNotificationFW() {
|
||||
func sendStopNotificationFW() {
|
||||
DistributedNotificationCenter.default().postNotificationName(Notification.Name("Pearcleaner.StopFileWatcher"), object: nil, userInfo: nil, deliverImmediately: true)
|
||||
}
|
||||
|
||||
|
||||
//func getCurrentTimestamp() -> String {
|
||||
// let dateFormatter = DateFormatter()
|
||||
// dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
|
||||
// return dateFormatter.string(from: Date())
|
||||
//}
|
||||
|
||||
|
||||
//func formattedDate(_ date: Date?) -> String {
|
||||
// guard let date = date else { return "N/A" }
|
||||
// let formatter = DateFormatter()
|
||||
// formatter.dateStyle = .short
|
||||
// formatter.timeStyle = .none
|
||||
// return formatter.string(from: date)
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ struct PearcleanerApp: App {
|
||||
@AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.miniview") private var miniView: Bool = true
|
||||
@AppStorage("settings.general.features") private var features: String = ""
|
||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||
@AppStorage("settings.menubar.mainWin") private var mainWinEnabled: Bool = false
|
||||
@@ -121,10 +120,9 @@ struct PearcleanerApp: App {
|
||||
updater.checkAndUpdateIfNeeded()
|
||||
|
||||
/// Get new features
|
||||
getFeatures(appState: appState, features: $features)
|
||||
updater.checkForAnnouncement()
|
||||
|
||||
// Make sure App Support folder exists in the future if needed for storage
|
||||
//MARK: This is not needed any longer as the update file is stored in /tmp directory. Use in the future for any local db functions the app might need
|
||||
// ensureApplicationSupportFolderExists(appState: appState)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xCF",
|
||||
"green" : "0xCF",
|
||||
"red" : "0xCF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x59",
|
||||
"green" : "0x59",
|
||||
"red" : "0x59"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x50",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x79"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x50",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x79"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF6",
|
||||
"green" : "0xF6",
|
||||
"red" : "0xF6"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x29",
|
||||
"green" : "0x28",
|
||||
"red" : "0x29"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ struct AboutSettingsTab: View {
|
||||
.padding(5)
|
||||
.padding(.leading)
|
||||
|
||||
Text("Made with ❤️ by Alin Lupascu").font(.footnote).padding(.bottom)
|
||||
Text("Made with ❤️ by Alin Lupascu").font(.footnote)//.padding(.bottom)
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 500)
|
||||
@@ -151,27 +151,6 @@ struct AboutSettingsTab: View {
|
||||
|
||||
|
||||
|
||||
extension Bundle {
|
||||
|
||||
var name: String {
|
||||
func string(for key: String) -> String? {
|
||||
object(forInfoDictionaryKey: key) as? String
|
||||
}
|
||||
return string(for: "CFBundleDisplayName")
|
||||
?? string(for: "CFBundleName")
|
||||
?? "N/A"
|
||||
}
|
||||
|
||||
var version: String {
|
||||
infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A"
|
||||
}
|
||||
|
||||
var buildVersion: String {
|
||||
infoDictionary?["CFBundleVersion"] as? String ?? "N/A"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: Sponsors
|
||||
struct Sponsor: Identifiable {
|
||||
let id = UUID()
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
// Created by Alin Lupascu on 3/20/24.
|
||||
//
|
||||
|
||||
//
|
||||
// General.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 11/5/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
@@ -15,24 +15,11 @@ struct GeneralSettingsTab: View {
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var themeManager: ThemeManager
|
||||
@State private var windowSettings = WindowSettings()
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.dark") var isDark: Bool = true
|
||||
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||
@AppStorage("settings.general.miniview") private var miniView: Bool = true
|
||||
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
|
||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||
@AppStorage("settings.general.oneshot") private var oneShotMode: Bool = false
|
||||
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||
@AppStorage("settings.general.sizeType") var sizeType: String = "Real"
|
||||
@State private var diskStatus: Bool = false
|
||||
@State private var accessStatus: Bool = false
|
||||
@State private var autoStatus: Bool = false
|
||||
@Binding var showPopover: Bool
|
||||
@Binding var search: String
|
||||
@State var selectedIndex: Int?
|
||||
@State private var isResetting = false
|
||||
|
||||
var body: some View {
|
||||
@@ -149,121 +136,6 @@ struct GeneralSettingsTab: View {
|
||||
.padding(.leading)
|
||||
|
||||
|
||||
// === Perms ================================================================================================
|
||||
|
||||
|
||||
// Divider()
|
||||
// .padding()
|
||||
|
||||
|
||||
// HStack() {
|
||||
// Text("Permissions").font(.title2)
|
||||
//
|
||||
//// InfoButtonPerms()
|
||||
//
|
||||
// Spacer()
|
||||
//
|
||||
// Button("Refresh") {
|
||||
// checkAllPermissions(appState: appState) { results in
|
||||
// updateOnMain {
|
||||
// appState.permissionResults = results
|
||||
// }
|
||||
// diskStatus = results.fullDiskAccess
|
||||
// accessStatus = results.accessibility
|
||||
// autoStatus = results.automation
|
||||
// if results.allPermissionsGranted {
|
||||
// updateOnMain {
|
||||
// appState.permissionsOkay = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", label: "Refresh", help: "Refresh permissions"))
|
||||
// .padding(.trailing, 5)
|
||||
//
|
||||
// }
|
||||
// .padding(.leading)
|
||||
|
||||
|
||||
// HStack(spacing: 0) {
|
||||
// Image(systemName: "externaldrive")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 20, height: 20)
|
||||
// .padding(.trailing)
|
||||
// .foregroundStyle(diskStatus ? .green : .red)
|
||||
// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
// Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted")
|
||||
// .font(.callout)
|
||||
// .foregroundStyle(.primary.opacity(0.5))
|
||||
//
|
||||
// Spacer()
|
||||
//
|
||||
// Button("") {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane"))
|
||||
//
|
||||
// }
|
||||
// .padding(5)
|
||||
// .padding(.leading)
|
||||
//
|
||||
//
|
||||
//
|
||||
// HStack(spacing: 0) {
|
||||
// Image(systemName: isVersionOrHigher(version: 14) ? "accessibility" : "figure.arms.open")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 20, height: 20)
|
||||
// .padding(.trailing)
|
||||
// .foregroundStyle(accessStatus ? .green : .red)
|
||||
// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
// Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted")
|
||||
// .font(.callout)
|
||||
// .foregroundStyle(.primary.opacity(0.5))
|
||||
//
|
||||
// Spacer()
|
||||
//
|
||||
// Button("") {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane"))
|
||||
//
|
||||
// }
|
||||
// .padding(5)
|
||||
// .padding(.leading)
|
||||
//
|
||||
//
|
||||
// HStack(spacing: 0) {
|
||||
// Image(systemName: "gearshape.2")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 20, height: 20)
|
||||
// .padding(.trailing)
|
||||
// .foregroundStyle(autoStatus ? .green : .red)
|
||||
// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
// Text(autoStatus ? "Automation permission granted" : "Automation permission not granted")
|
||||
// .font(.callout)
|
||||
// .foregroundStyle(.primary.opacity(0.5))
|
||||
//
|
||||
// Spacer()
|
||||
//
|
||||
// Button("") {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View automation permissions pane"))
|
||||
//
|
||||
// }
|
||||
// .padding(5)
|
||||
// .padding(.leading)
|
||||
|
||||
|
||||
|
||||
// === Sentinel =============================================================================================
|
||||
|
||||
@@ -283,7 +155,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.foregroundStyle(sentinel ? .green : .red)
|
||||
.saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
.saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
@@ -324,7 +196,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.foregroundStyle(appState.finderExtensionEnabled ? .green : .red)
|
||||
.saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
.saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1)
|
||||
Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
|
||||
@@ -17,17 +17,11 @@ struct InterfaceSettingsTab: View {
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@EnvironmentObject var themeManager: ThemeManager
|
||||
@State private var windowSettings = WindowSettings()
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
// @AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||
// @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||
@AppStorage("settings.general.dark") var isDark: Bool = true
|
||||
// @AppStorage("settings.interface.themesEnabled") var themesEnabled: Bool = false
|
||||
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||
@AppStorage("settings.general.miniview") private var miniView: Bool = true
|
||||
// @AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
|
||||
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4"
|
||||
@State private var isLaunchAtLoginEnabled: Bool = false
|
||||
@Binding var showPopover: Bool
|
||||
|
||||
@@ -21,7 +21,7 @@ struct SettingsView: View {
|
||||
var body: some View {
|
||||
|
||||
TabView(selection: $selectedTab) {
|
||||
GeneralSettingsTab(showPopover: $showPopover, search: $search)
|
||||
GeneralSettingsTab()
|
||||
.tabItem {
|
||||
Label(CurrentTabView.general.title, systemImage: "gear")
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ struct TipsSettingsTab: View {
|
||||
.padding()
|
||||
}
|
||||
.scrollIndicators(.visible)
|
||||
.frame(minHeight: 0, maxHeight: 500)
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.frame(height: 500)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(backgroundView(themeManager: themeManager, darker: true))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
.padding(.bottom)
|
||||
@@ -47,7 +47,7 @@ struct TipsSettingsTab: View {
|
||||
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 500)//, height: 520)
|
||||
.frame(width: 500)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ struct UpdateSettingsTab: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeManager: ThemeManager
|
||||
@EnvironmentObject var updater: Updater
|
||||
@State private var showAlert = false
|
||||
@State private var showDone = false
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@@ -25,46 +23,36 @@ struct UpdateSettingsTab: View {
|
||||
.padding(.horizontal)
|
||||
|
||||
ReleasesView(updater: updater)
|
||||
.frame(maxWidth: .infinity, maxHeight: 400)
|
||||
.frame(height: 400)
|
||||
.frame(maxWidth: .infinity)
|
||||
.backgroundAF(opacity: 0.5)
|
||||
|
||||
|
||||
HStack(alignment: .center, spacing: 20) {
|
||||
Spacer()
|
||||
|
||||
Button(""){
|
||||
updater.checkForUpdates(showSheet: false)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Refresh updater"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", label: "Refresh", help: "Refresh updater"))
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(""){
|
||||
NewWin.show(appState: appState, width: 500, height: 440, newWin: .feature)
|
||||
updater.resetAnnouncementAlert()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "star", label: "Announcement", help: "Show announcements badge again"))
|
||||
|
||||
Spacer()
|
||||
|
||||
// Button(""){
|
||||
// updater.checkForUpdates(showSheet: true)
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonStyle(icon: "arrow.down.square", help: "Check for updates"))
|
||||
//
|
||||
// Spacer()
|
||||
|
||||
Button(""){
|
||||
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View releases on GitHub"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "link", label: "Releases", help: "View releases on GitHub"))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 500)//, height: 520)
|
||||
.frame(width: 500)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ struct AppListItems: View {
|
||||
HStack {
|
||||
Spacer()
|
||||
RoundedRectangle(cornerRadius: 50)
|
||||
.fill(isSelected ? Color("pear") : .primary.opacity(0.5))
|
||||
.fill(isSelected ? Color("AccentColor") : .primary.opacity(0.5))
|
||||
.frame(width: isSelected ? 4 : 2, height: 25)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
|
||||
@@ -16,18 +16,13 @@ struct AppSearchView: View {
|
||||
@EnvironmentObject var updater: Updater
|
||||
@EnvironmentObject var permissionManager: PermissionManager
|
||||
var glass: Bool
|
||||
var sidebarWidth: Double
|
||||
var menubarEnabled: Bool
|
||||
var mini: Bool
|
||||
@Binding var search: String
|
||||
@Binding var showPopover: Bool
|
||||
@State private var showMenu = false
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@State private var showFeatures: Bool = false
|
||||
@Binding var isMenuBar: Bool
|
||||
@AppStorage("settings.general.selectedSortAppsList") var selectedSortAlpha: Bool = true
|
||||
@State private var progress: Double = 0.0
|
||||
|
||||
|
||||
var body: some View {
|
||||
@@ -37,24 +32,17 @@ struct AppSearchView: View {
|
||||
.padding(.top, !isMenuBar ? 25 : 0)
|
||||
|
||||
if updater.updateAvailable {
|
||||
UpdateButton(updater: updater, dark: false, opacity: 1)
|
||||
UpdateBadge(updater: updater)
|
||||
.padding(.horizontal)
|
||||
} else if let _ = permissionManager.results, !permissionManager.allPermissionsGranted {
|
||||
PermissionsView(dark: false, opacity: 1)
|
||||
PermissionsBadge()
|
||||
.padding(.horizontal)
|
||||
} else if appState.featureAvailable {
|
||||
AlertNotification(label: "New Message!", icon: "star", buttonAction: {
|
||||
showFeatures.toggle()
|
||||
}, btnColor: Color.blue, opacity: 1, themeManager: themeManager)
|
||||
} else if updater.announcementAvailable {
|
||||
FeatureBadge(updater: updater)
|
||||
.padding(.horizontal)
|
||||
.sheet(isPresented: $showFeatures, content: {
|
||||
FeatureView()
|
||||
.environmentObject(themeManager)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps)
|
||||
|
||||
Divider()
|
||||
|
||||
@@ -53,11 +53,8 @@ struct MiniEmptyView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
// @State private var animateGradient: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
// @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
|
||||
@Binding var showPopover: Bool
|
||||
// @State private var animationStart = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
@@ -75,49 +72,6 @@ struct MiniEmptyView: View {
|
||||
.offset(x: 5, y: 5)
|
||||
)
|
||||
|
||||
// if #available(macOS 14, *) {
|
||||
// if animateLogo && animationStart {
|
||||
// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
|
||||
// .mask(
|
||||
// Image(systemName: "plus.square.dashed")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 120, height: 120, alignment: .center)
|
||||
// .padding()
|
||||
// .fontWeight(.ultraLight)
|
||||
// .offset(x: 5, y: 5)
|
||||
// )
|
||||
// .phaseAnimator([false, true]) { wwdc24, chromaRotate in
|
||||
// wwdc24
|
||||
// .hueRotation(.degrees(chromaRotate ? 420 : 0))
|
||||
// } animation: { chromaRotate in
|
||||
// .easeInOut(duration: 6)
|
||||
// }
|
||||
// } else {
|
||||
// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
|
||||
// .mask(
|
||||
// Image(systemName: "plus.square.dashed")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 120, height: 120, alignment: .center)
|
||||
// .padding()
|
||||
// .fontWeight(.ultraLight)
|
||||
// .offset(x: 5, y: 5)
|
||||
// )
|
||||
// }
|
||||
// } else {
|
||||
// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
|
||||
// .mask(
|
||||
// Image(systemName: "plus.square.dashed")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 120, height: 120, alignment: .center)
|
||||
// .padding()
|
||||
// .fontWeight(.ultraLight)
|
||||
// .offset(x: 5, y: 5)
|
||||
// )
|
||||
// }
|
||||
|
||||
Text("Drop an app here")
|
||||
.font(.title3)
|
||||
.opacity(0.7)
|
||||
@@ -136,11 +90,6 @@ struct MiniEmptyView: View {
|
||||
appState.currentView = .apps
|
||||
}
|
||||
}
|
||||
// .onAppear {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
// animationStart = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +102,6 @@ struct MiniAppView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var themeManager: ThemeManager
|
||||
// @State private var animateGradient: Bool = false
|
||||
@Binding var search: String
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@@ -184,7 +132,7 @@ struct MiniAppView: View {
|
||||
}
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar)
|
||||
AppSearchView(glass: glass, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// SegmentedPicker.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 4/17/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct SegmentedPicker<Element, Content, Selection>: View
|
||||
where
|
||||
Content: View,
|
||||
Selection: View {
|
||||
|
||||
public typealias Data = [Element]
|
||||
|
||||
@State private var frames: [CGRect]
|
||||
@Binding private var selectedIndex: Data.Index?
|
||||
|
||||
private let data: Data
|
||||
private let selection: () -> Selection
|
||||
private let content: (Data.Element, Bool) -> Content
|
||||
private let selectionAlignment: VerticalAlignment
|
||||
|
||||
public init(_ data: Data,
|
||||
selectedIndex: Binding<Data.Index?>,
|
||||
selectionAlignment: VerticalAlignment = .center,
|
||||
@ViewBuilder content: @escaping (Data.Element, Bool) -> Content,
|
||||
@ViewBuilder selection: @escaping () -> Selection) {
|
||||
|
||||
self.data = data
|
||||
self.content = content
|
||||
self.selection = selection
|
||||
self._selectedIndex = selectedIndex
|
||||
self._frames = State(wrappedValue: Array(repeating: .zero,
|
||||
count: data.count))
|
||||
self.selectionAlignment = selectionAlignment
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack(alignment: Alignment(horizontal: .horizontalCenterAlignment,
|
||||
vertical: selectionAlignment)) {
|
||||
|
||||
if let selectedIndex = selectedIndex {
|
||||
selection()
|
||||
.frame(width: frames[selectedIndex].width,
|
||||
height: frames[selectedIndex].height)
|
||||
.alignmentGuide(.horizontalCenterAlignment) { dimensions in
|
||||
dimensions[HorizontalAlignment.center]
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 0) {
|
||||
ForEach(data.indices, id: \.self) { index in
|
||||
Button(action: { selectedIndex = index },
|
||||
label: { content(data[index], selectedIndex == index) }
|
||||
)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.background(GeometryReader { proxy in
|
||||
Color.clear.onAppear { frames[index] = proxy.frame(in: .global) }
|
||||
})
|
||||
.alignmentGuide(.horizontalCenterAlignment,
|
||||
isActive: selectedIndex == index) { dimensions in
|
||||
dimensions[HorizontalAlignment.center]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension HorizontalAlignment {
|
||||
private enum CenterAlignmentID: AlignmentID {
|
||||
static func defaultValue(in dimension: ViewDimensions) -> CGFloat {
|
||||
return dimension[HorizontalAlignment.center]
|
||||
}
|
||||
}
|
||||
|
||||
static var horizontalCenterAlignment: HorizontalAlignment {
|
||||
HorizontalAlignment(CenterAlignmentID.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension View {
|
||||
@ViewBuilder
|
||||
@inlinable func alignmentGuide(_ alignment: HorizontalAlignment,
|
||||
isActive: Bool,
|
||||
computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View {
|
||||
if isActive {
|
||||
alignmentGuide(alignment, computeValue: computeValue)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@inlinable func alignmentGuide(_ alignment: VerticalAlignment,
|
||||
isActive: Bool,
|
||||
computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View {
|
||||
|
||||
if isActive {
|
||||
alignmentGuide(alignment, computeValue: computeValue)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SegmentedPicker(
|
||||
// ["Alpha", "Size"],
|
||||
// selectedIndex: Binding(
|
||||
// get: { selectedSortAlpha ? 0 : 1 },
|
||||
// set: { newIndex in
|
||||
// withAnimation(.easeInOut(duration: 0.3)) {
|
||||
// selectedSortAlpha = (newIndex == 0)
|
||||
// }
|
||||
// }),
|
||||
// selectionAlignment: .bottom,
|
||||
// content: { item, isSelected in
|
||||
// Text(item)
|
||||
// .font(.callout)
|
||||
// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5))
|
||||
// .padding(.horizontal)
|
||||
// .padding(.bottom, 5)
|
||||
// .frame(width: 75)
|
||||
//
|
||||
// },
|
||||
// selection: {
|
||||
// VStack(spacing: 0) {
|
||||
// Spacer()
|
||||
// Color("pear").frame(height: 1)
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
// SegmentedPicker(
|
||||
// ["Real", "Logical", "Finder"],
|
||||
// selectedIndex: Binding(
|
||||
// get: {
|
||||
// switch sizeType {
|
||||
// case "Real": return 0
|
||||
// case "Logical": return 1
|
||||
// case "Finder": return 2
|
||||
// default: return 0
|
||||
// }
|
||||
// },
|
||||
// set: { newIndex in
|
||||
// withAnimation(.easeInOut(duration: 0.3)) {
|
||||
// switch newIndex {
|
||||
// case 0: sizeType = "Real"
|
||||
// case 1: sizeType = "Logical"
|
||||
// case 2: sizeType = "Finder"
|
||||
// default: sizeType = "Real"
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// selectionAlignment: .bottom,
|
||||
// content: { item, isSelected in
|
||||
// Text(item)
|
||||
// .font(.callout)
|
||||
// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5) )
|
||||
// .padding(.horizontal)
|
||||
// .padding(.bottom, 5)
|
||||
// .frame(width: 75)
|
||||
// },
|
||||
// selection: {
|
||||
// VStack(spacing: 0) {
|
||||
// Spacer()
|
||||
// Color("pear").frame(height: 1)
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
|
||||
// SegmentedPicker(
|
||||
// ["Auto", "Dark", "Light"],
|
||||
// selectedIndex: Binding(
|
||||
// get: {
|
||||
// switch selectedTheme {
|
||||
// case "Dark": return 1
|
||||
// case "Light": return 2
|
||||
// default: return 0
|
||||
// }
|
||||
// },
|
||||
// set: { newIndex in
|
||||
// withAnimation(.easeInOut(duration: 0.3)) {
|
||||
// switch newIndex {
|
||||
// case 1: selectedTheme = "Dark"
|
||||
// case 2: selectedTheme = "Light"
|
||||
// default: selectedTheme = "Auto"
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// selectionAlignment: .bottom,
|
||||
// content: { item, isSelected in
|
||||
// Text(item)
|
||||
// .font(.callout)
|
||||
// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5) )
|
||||
// .padding(.horizontal)
|
||||
// .padding(.bottom, 5)
|
||||
// .frame(width: 65)
|
||||
// },
|
||||
// selection: {
|
||||
// VStack(spacing: 0) {
|
||||
// Spacer()
|
||||
// Color("pear").frame(height: 1)
|
||||
// }
|
||||
// })
|
||||
@@ -45,7 +45,7 @@ struct RegularMode: View {
|
||||
.frame(width: sidebarWidth)
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar)
|
||||
AppSearchView(glass: glass, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar)
|
||||
.frame(width: sidebarWidth)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
//
|
||||
// FeatureView.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 5/5/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AlinFoundation
|
||||
|
||||
struct FeatureView: View {
|
||||
@EnvironmentObject var themeManager: ThemeManager
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@AppStorage("settings.general.features") private var features: String = ""
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 5) {
|
||||
HStack {
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!") .font(.title)
|
||||
.bold()
|
||||
// .padding(.bottom)
|
||||
|
||||
Spacer()
|
||||
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding([.vertical])
|
||||
|
||||
Text(features)
|
||||
.font(.body)
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(.horizontal)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(alignment: .center, spacing: 20) {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
// NewWin.close()
|
||||
}) {
|
||||
Text("Close")
|
||||
}
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .primary))
|
||||
}
|
||||
.padding(.vertical)
|
||||
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(themeManager.pickerColor)
|
||||
|
||||
// .padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
//
|
||||
// NewWin.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 11/16/23.
|
||||
//
|
||||
|
||||
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
class NewWin: NSWindowController {
|
||||
|
||||
static var shared = NewWin()
|
||||
|
||||
static func show(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) {
|
||||
shared = NewWin(appState: appState, width: width, height: height, newWin: newWin)
|
||||
shared.window?.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
static func close() {
|
||||
shared.window?.close()
|
||||
}
|
||||
|
||||
convenience init(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) {
|
||||
|
||||
let window = Self.makeWindow(width: width, height: height)
|
||||
|
||||
window.backgroundColor = NSColor.controlBackgroundColor
|
||||
|
||||
self.init(window: window)
|
||||
|
||||
// Using Visual Effect to make titlebar fully transparent
|
||||
let visualEffect = NSVisualEffectView()
|
||||
visualEffect.blendingMode = .behindWindow
|
||||
visualEffect.state = .active
|
||||
visualEffect.material = .underWindowBackground
|
||||
|
||||
let contentView = makeNewView(appState: appState, newWin: newWin)
|
||||
|
||||
let hostView = NSHostingView(rootView: contentView)
|
||||
|
||||
window.contentView = visualEffect
|
||||
|
||||
visualEffect.addSubview(hostView)
|
||||
hostView.frame = visualEffect.frame
|
||||
window.titleVisibility = .hidden
|
||||
window.titlebarAppearsTransparent = true
|
||||
window.center()
|
||||
window.title = "Pearcleaner"
|
||||
|
||||
}
|
||||
|
||||
private static func makeWindow(width: CGFloat, height: CGFloat) -> NSWindow {
|
||||
let contentRect = NSRect(x: 0, y: 0, width: width, height: height)
|
||||
let styleMask: NSWindow.StyleMask = [
|
||||
.titled,
|
||||
.fullSizeContentView,
|
||||
// .closable,
|
||||
]
|
||||
return NSWindow(contentRect: contentRect,
|
||||
styleMask: styleMask,
|
||||
backing: .buffered,
|
||||
defer: false)
|
||||
}
|
||||
|
||||
private func makeNewView(appState: AppState, newWin: NewWindow) -> some View {
|
||||
|
||||
switch newWin {
|
||||
case .perm:
|
||||
return AnyView(PermView()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.black.opacity(0.2))
|
||||
.environmentObject(appState))
|
||||
case .feature:
|
||||
return AnyView(FeatureView()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(.black.opacity(0.2))
|
||||
.environmentObject(appState))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
//
|
||||
// PermView.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 11/16/23.
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PermView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("Pearcleaner Permissions")
|
||||
.font(.title)
|
||||
.bold()
|
||||
.padding(.vertical)
|
||||
.padding(.top, 5)
|
||||
|
||||
Spacer()
|
||||
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding(.bottom)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
Image(systemName: "externaldrive")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
Text("Full Disk Access permission to find and delete files in system paths")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
Image(systemName: "accessibility")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
Text("Accessibility permission to delete files via Finder")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
Image(systemName: "gearshape.2")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
Text("Automation permission to perform extension actions via Finder")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
Image(systemName: "calendar")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
Text("Reminders permission to find and delete some reminder files that certain apps cache")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
Image(systemName: "info.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
Text("Add Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On if neeeded. Restart app when both permissions are granted")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
|
||||
if #available(macOS 14.0, *) {
|
||||
SettingsLink {}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "gear", label: "Settings", help: "Check permissions in Settings", color: .primary))
|
||||
} else {
|
||||
Button("Settings") {
|
||||
if #available(macOS 13.0, *) {
|
||||
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
|
||||
}
|
||||
else {
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "gear", label: "Settings", help: "Check permissions in Settings", color: .red))
|
||||
}
|
||||
|
||||
|
||||
// Button(action: {
|
||||
// relaunchApp(afterDelay: 1)
|
||||
// }) {
|
||||
// Text("Restart")
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", label: "Restart", help: "Restart", color: .red))
|
||||
// .padding()
|
||||
|
||||
Button(action: {
|
||||
NewWin.close()
|
||||
}) {
|
||||
Text("Close")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .blue))
|
||||
.padding()
|
||||
|
||||
// Button(action: {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }) {
|
||||
// Text("Full Disk Access")
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", label: "Full Disk", help: "Full Disk", color: .accentColor))
|
||||
// .padding()
|
||||
//
|
||||
// Button(action: {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }) {
|
||||
// Text("Accessibility")
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", label: "Accessibility", help: "Accessibility", color: .accentColor))
|
||||
// .padding()
|
||||
//
|
||||
// Button("") {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "gearshape.2", label: "Automation", help: "Automation", color: .accentColor))
|
||||
// .padding()
|
||||
//
|
||||
// Button("") {
|
||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Reminders") {
|
||||
// NSWorkspace.shared.open(url)
|
||||
// }
|
||||
// }
|
||||
// .buttonStyle(SimpleButtonBrightStyle(icon: "calendar", label: "Reminders", help: "Reminders", color: .accentColor))
|
||||
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
selectedTab = .general
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ This project was born out of wanting to learn more on how macOS deals with app i
|
||||
## Features
|
||||
- Signed/notarized
|
||||
- Swift/SwiftUI
|
||||
- Small app size (~3MB)
|
||||
- Small app size (~4MB)
|
||||
- Leftover file search for finding remaining files from previously uninstalled applications
|
||||
- Sentinel monitor helper that can be enabled to watch Trash folder for deleted apps to cleanup after the fact(Extremely small (210KB) and uses ~2mb of ram to run in the background and file watch)
|
||||
- Mini mode which can be enabled from Settings
|
||||
|
||||
Reference in New Issue
Block a user