This commit is contained in:
Alin
2024-04-12 16:44:53 -06:00
parent 9124d85c5d
commit bf7771cf61
28 changed files with 1014 additions and 412 deletions
+14
View File
@@ -0,0 +1,14 @@
<?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-write</key>
<true/>
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<array>
<string>/</string>
</array>
</dict>
</plist>
+131
View File
@@ -0,0 +1,131 @@
//
// FinderSync.swift
// FinderOpen
//
// Created by Alin Lupascu on 4/11/24.
//
import Cocoa
import FinderSync
class FinderOpen: FIFinderSync {
override init() {
super.init()
NSLog("FinderSync() launched from %@", Bundle.main.bundlePath as NSString)
// Set the directory URLs that the Finder Sync extension observes
FIFinderSyncController.default().directoryURLs = Set([URL(fileURLWithPath: "/")])
}
override func menu(for menuKind: FIMenuKind) -> NSMenu {
let menu = NSMenu(title: "")
// Ensure we are dealing with the contextual menu for items
if menuKind == .contextualMenuForItems {
// Get the selected items
if let selectedItemURLs = FIFinderSyncController.default().selectedItemURLs(),
selectedItemURLs.count == 1, selectedItemURLs.first?.pathExtension == "app" {
// Add menu item if the selected item is a .app file
let menuItem = NSMenuItem(title: "Pearcleaner Uninstall", action: #selector(openInMyApp), keyEquivalent: "")
// menuItem.image = NSImage(named: NSImage.trashEmptyName)
// menu.addItem(withTitle: "Pearcleaner Uninstall", action: #selector(openInMyApp), keyEquivalent: "")
menu.addItem(menuItem)
}
}
// Return the menu (which may be empty if the conditions are not met)
return menu
}
@objc func openInMyApp(_ sender: AnyObject?) {
// Get the selected items (files/folders) in Finder
guard let selectedItems = FIFinderSyncController.default().selectedItemURLs(), !selectedItems.isEmpty else {
return
}
// Consider only the first selected item
let firstSelectedItem = selectedItems[0]
let path = firstSelectedItem.path
NSWorkspace.shared.open(URL(string: "pear://com.alienator88.Pearcleaner?path=\(path)")!)
}
}
//class FinderSync: FIFinderSync {
//
// var myFolderURL = URL(fileURLWithPath: "/Users/Shared/MySyncExtension Documents")
//
// override init() {
// super.init()
//
// NSLog("FinderSync() launched from %@", Bundle.main.bundlePath as NSString)
//
// // Set up the directory we are syncing.
// FIFinderSyncController.default().directoryURLs = [self.myFolderURL]
//
// // Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
// FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.colorPanelName)!, label: "Status One" , forBadgeIdentifier: "One")
// FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.cautionName)!, label: "Status Two", forBadgeIdentifier: "Two")
// }
//
// // MARK: - Primary Finder Sync protocol methods
//
// override func beginObservingDirectory(at url: URL) {
// // The user is now seeing the container's contents.
// // If they see it in more than one view at a time, we're only told once.
// NSLog("beginObservingDirectoryAtURL: %@", url.path as NSString)
// }
//
//
// override func endObservingDirectory(at url: URL) {
// // The user is no longer seeing the container's contents.
// NSLog("endObservingDirectoryAtURL: %@", url.path as NSString)
// }
//
// override func requestBadgeIdentifier(for url: URL) {
// NSLog("requestBadgeIdentifierForURL: %@", url.path as NSString)
//
// // For demonstration purposes, this picks one of our two badges, or no badge at all, based on the filename.
// let whichBadge = abs(url.path.hash) % 3
// let badgeIdentifier = ["", "One", "Two"][whichBadge]
// FIFinderSyncController.default().setBadgeIdentifier(badgeIdentifier, for: url)
// }
//
// // MARK: - Menu and toolbar item support
//
// override var toolbarItemName: String {
// return "FinderSy"
// }
//
// override var toolbarItemToolTip: String {
// return "FinderSy: Click the toolbar item for a menu."
// }
//
// override var toolbarItemImage: NSImage {
// return NSImage(named: NSImage.cautionName)!
// }
//
// override func menu(for menuKind: FIMenuKind) -> NSMenu {
// // Produce a menu for the extension.
// let menu = NSMenu(title: "")
// menu.addItem(withTitle: "Example Menu Item", action: #selector(sampleAction(_:)), keyEquivalent: "")
// return menu
// }
//
// @IBAction func sampleAction(_ sender: AnyObject?) {
// let target = FIFinderSyncController.default().targetedURL()
// let items = FIFinderSyncController.default().selectedItemURLs()
//
// let item = sender as! NSMenuItem
// NSLog("sampleAction: menu item: %@, target = %@, items = ", item.title as NSString, target!.path as NSString)
// for obj in items! {
// NSLog(" %@", obj.path as NSString)
// }
// }
//
//}
+19
View File
@@ -0,0 +1,19 @@
<?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>CFBundleShortVersionString</key>
<string>$(APP_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(APP_BUILD)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict/>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.FinderSync</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).FinderOpen</string>
</dict>
</dict>
</plist>
+182 -5
View File
@@ -29,6 +29,9 @@
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B901B2AF193A3009CC655 /* Styles.swift */; };
C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */; };
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90222AF2D616009CC655 /* FilesView.swift */; };
C78121662BC892A000BE06BD /* FinderOpen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C78121652BC892A000BE06BD /* FinderOpen.swift */; };
C781216B2BC892A000BE06BD /* FinderOpen.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C78121632BC892A000BE06BD /* FinderOpen.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
C79D05202BC99EB40083F976 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79D051F2BC99EB40083F976 /* ThemeManager.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 */; };
@@ -49,6 +52,16 @@
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
C78121692BC892A000BE06BD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C77B8FF82AF18E2E009CC655 /* Project object */;
proxyType = 1;
remoteGlobalIDString = C78121622BC892A000BE06BD;
remoteInfo = FinderOpen;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
C728930D2AFD51EA00C8C1CD /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
@@ -59,6 +72,17 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
C781216C2BC892A000BE06BD /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
C781216B2BC892A000BE06BD /* FinderOpen.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
C7A27E7D2AFD65C400166168 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -97,6 +121,11 @@
C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = "<group>"; };
C77B90222AF2D616009CC655 /* FilesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesView.swift; sourceTree = "<group>"; };
C77B90242AF2D796009CC655 /* Pearcleaner.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Pearcleaner.entitlements; sourceTree = "<group>"; };
C78121632BC892A000BE06BD /* FinderOpen.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FinderOpen.appex; sourceTree = BUILT_PRODUCTS_DIR; };
C78121652BC892A000BE06BD /* FinderOpen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderOpen.swift; sourceTree = "<group>"; };
C78121672BC892A000BE06BD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C78121682BC892A000BE06BD /* FinderOpen.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FinderOpen.entitlements; sourceTree = "<group>"; };
C79D051F2BC99EB40083F976 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.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>"; };
@@ -133,6 +162,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C78121602BC892A000BE06BD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -164,6 +200,7 @@
C7A9CE462B89164700EB6E78 /* Authorization.swift */,
C7ACADE52B92A737000B5845 /* Features.swift */,
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */,
C79D051F2BC99EB40083F976 /* ThemeManager.swift */,
);
path = Logic;
sourceTree = "<group>";
@@ -214,6 +251,7 @@
children = (
C77B90022AF18E2E009CC655 /* Pearcleaner */,
C72893102AFD51EA00C8C1CD /* PearcleanerSentinel */,
C78121642BC892A000BE06BD /* FinderOpen */,
C77B90012AF18E2E009CC655 /* Products */,
C72893162AFD531500C8C1CD /* Frameworks */,
);
@@ -224,6 +262,7 @@
children = (
C77B90002AF18E2E009CC655 /* Pearcleaner.app */,
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */,
C78121632BC892A000BE06BD /* FinderOpen.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -257,6 +296,16 @@
path = Views;
sourceTree = "<group>";
};
C78121642BC892A000BE06BD /* FinderOpen */ = {
isa = PBXGroup;
children = (
C78121652BC892A000BE06BD /* FinderOpen.swift */,
C78121672BC892A000BE06BD /* Info.plist */,
C78121682BC892A000BE06BD /* FinderOpen.entitlements */,
);
path = FinderOpen;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -288,10 +337,12 @@
C77B8FFD2AF18E2E009CC655 /* Frameworks */,
C77B8FFE2AF18E2E009CC655 /* Resources */,
C7A27E7D2AFD65C400166168 /* CopyFiles */,
C781216C2BC892A000BE06BD /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
C781216A2BC892A000BE06BD /* PBXTargetDependency */,
);
name = Pearcleaner;
packageProductDependencies = (
@@ -300,6 +351,23 @@
productReference = C77B90002AF18E2E009CC655 /* Pearcleaner.app */;
productType = "com.apple.product-type.application";
};
C78121622BC892A000BE06BD /* FinderOpen */ = {
isa = PBXNativeTarget;
buildConfigurationList = C781216F2BC892A000BE06BD /* Build configuration list for PBXNativeTarget "FinderOpen" */;
buildPhases = (
C781215F2BC892A000BE06BD /* Sources */,
C78121602BC892A000BE06BD /* Frameworks */,
C78121612BC892A000BE06BD /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = FinderOpen;
productName = FinderOpen;
productReference = C78121632BC892A000BE06BD /* FinderOpen.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -307,7 +375,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1500;
LastSwiftUpdateCheck = 1530;
LastUpgradeCheck = 1510;
TargetAttributes = {
C728930E2AFD51EA00C8C1CD = {
@@ -316,6 +384,9 @@
C77B8FFF2AF18E2E009CC655 = {
CreatedOnToolsVersion = 15.0.1;
};
C78121622BC892A000BE06BD = {
CreatedOnToolsVersion = 15.3;
};
};
};
buildConfigurationList = C77B8FFB2AF18E2E009CC655 /* Build configuration list for PBXProject "Pearcleaner" */;
@@ -336,6 +407,7 @@
targets = (
C77B8FFF2AF18E2E009CC655 /* Pearcleaner */,
C728930E2AFD51EA00C8C1CD /* PearcleanerSentinel */,
C78121622BC892A000BE06BD /* FinderOpen */,
);
};
/* End PBXProject section */
@@ -351,6 +423,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C78121612BC892A000BE06BD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -393,6 +472,7 @@
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */,
C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */,
C74412C72BBF249000DDFCA8 /* AppPathsFetch.swift in Sources */,
C79D05202BC99EB40083F976 /* ThemeManager.swift in Sources */,
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */,
C7ACADE62B92A737000B5845 /* Features.swift in Sources */,
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */,
@@ -403,8 +483,24 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C781215F2BC892A000BE06BD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C78121662BC892A000BE06BD /* FinderOpen.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
C781216A2BC892A000BE06BD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C78121622BC892A000BE06BD /* FinderOpen */;
targetProxy = C78121692BC892A000BE06BD /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
C72893142AFD51EA00C8C1CD /* Debug */ = {
isa = XCBuildConfiguration;
@@ -412,6 +508,7 @@
CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
@@ -430,6 +527,7 @@
CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
@@ -446,6 +544,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 34;
APP_VERSION = 3.5.0;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -477,6 +577,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 33;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -499,6 +600,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -512,6 +614,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 34;
APP_VERSION = 3.5.0;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -543,6 +647,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 33;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
@@ -559,6 +664,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
@@ -569,13 +675,14 @@
C77B90102AF18E2F009CC655 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 33;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU;
@@ -593,7 +700,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -604,13 +711,14 @@
C77B90112AF18E2F009CC655 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 33;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU;
@@ -628,7 +736,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -636,6 +744,66 @@
};
name = Release;
};
C781216D2BC892A000BE06BD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = FinderOpen/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = FinderOpen;
INFOPLIST_KEY_LSApplicationCategoryType = "";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner.FinderOpen;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
C781216E2BC892A000BE06BD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = FinderOpen/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = FinderOpen;
INFOPLIST_KEY_LSApplicationCategoryType = "";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner.FinderOpen;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -666,6 +834,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C781216F2BC892A000BE06BD /* Build configuration list for PBXNativeTarget "FinderOpen" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C781216D2BC892A000BE06BD /* Debug */,
C781216E2BC892A000BE06BD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C78121622BC892A000BE06BD"
BuildableName = "FinderOpen.appex"
BlueprintName = "FinderOpen"
ReferencedContainer = "container:Pearcleaner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C77B8FFF2AF18E2E009CC655"
BuildableName = "Pearcleaner.app"
BlueprintName = "Pearcleaner"
ReferencedContainer = "container:Pearcleaner.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 = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.finder"
RemotePath = "/Finder">
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C77B8FFF2AF18E2E009CC655"
BuildableName = "Pearcleaner.app"
BlueprintName = "Pearcleaner"
ReferencedContainer = "container:Pearcleaner.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C77B8FFF2AF18E2E009CC655"
BuildableName = "Pearcleaner.app"
BlueprintName = "Pearcleaner"
ReferencedContainer = "container:Pearcleaner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+5 -4
View File
@@ -41,15 +41,16 @@ struct AppCommands: Commands {
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps = []
// appState.sortedApps.systemApps = []
appState.sortedApps = sortedApps
// appState.sortedApps.systemApps = sortedApps.systemApps
if instantSearch {
Task(priority: .high){
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations)
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
}
} else {
appState.reload.toggle()
}
appState.reload.toggle()
}
}
} label: {
+17
View File
@@ -7,6 +7,7 @@
import Foundation
import SwiftUI
import FinderSync
let home = FileManager.default.homeDirectoryForCurrentUser.path
@@ -31,6 +32,7 @@ class AppState: ObservableObject
@Published var popCount: Int = 0
@Published var instantProgress: Double = 0.0
@Published var instantTotal: Double = 0.0
@Published var finderExtensionEnabled: Bool = false
init() {
@@ -54,6 +56,21 @@ class AppState: ObservableObject
fileSize: [:],
fileIcon: [:]
)
updateExtensionStatus()
NotificationCenter.default.addObserver(
self,
selector: #selector(updateExtensionStatus),
name: NSApplication.didBecomeActiveNotification,
object: nil
)
}
@objc func updateExtensionStatus() {
let extentionStatus = FIFinderSyncController.isExtensionEnabled
DispatchQueue.main.async {
self.finderExtensionEnabled = extentionStatus
}
}
}
-1
View File
@@ -26,7 +26,6 @@ class DeeplinkManager {
// This handles dropping an app onto Pearcleaner
if url.pathExtension == "app" {
handleAppBundle(url: url, appState: appState, locations: locations)
// This handles sentinel monitor launch
} else if url.scheme == DeepLinkConstants.scheme,
url.host == DeepLinkConstants.host,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
+76 -65
View File
@@ -11,15 +11,17 @@ import SwiftUI
struct SimpleButtonStyle: ButtonStyle {
@State private var hovered = false
let icon: String
let label: String?
let label: String
let help: String
let color: Color
let size: CGFloat
init(icon: String, label: String? = "", help: String, color: Color) {
init(icon: String, label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20) {
self.icon = icon
self.label = label
self.help = help
self.color = color
self.size = size
}
func makeBody(configuration: Self.Configuration) -> some View {
@@ -27,8 +29,8 @@ struct SimpleButtonStyle: ButtonStyle {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(width: 20)
if let label = label, !label.isEmpty {
.frame(width: size, height: size)
if !label.isEmpty {
Text(label)
}
}
@@ -45,34 +47,6 @@ struct SimpleButtonStyle: ButtonStyle {
}
struct NavButtonBottomBarStyle: ButtonStyle {
@State private var isHovered = false
var image: String
var help: String
func makeBody(configuration: Configuration) -> some View {
ZStack {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
Rectangle()
.foregroundColor(.clear)
.frame(width: 36, height: 36)
.cornerRadius(6)
.contentShape(Rectangle())
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.buttonStyle(.plain)
.onHover { inside in
isHovered = inside
}
.help(help)
}
}
struct InfoButton: View {
@State private var isPopoverPresented: Bool = false
@@ -215,7 +189,7 @@ public struct NewFeatureView: View {
showFeature = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: .gray))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: (Color("mode").opacity(0.5))))
.onHover { isHovered in
if isHovered {
NSCursor.pointingHand.push()
@@ -249,43 +223,36 @@ public struct NewFeatureView: View {
public struct SlideableDivider: View {
@EnvironmentObject private var themeSettings: ThemeSettings
@Binding var dimension: Double
@State private var dimensionStart: Double?
@State private var handleWidth: Double = 2
@State private var handleWidth: Double = 4
@State private var handleHeight: Double = 30
@State private var isHovered: Bool = false
public init(dimension: Binding<Double>) {
self._dimension = dimension
}
public var body: some View {
Rectangle()
.foregroundStyle(Color.gray.opacity(0.3))
.frame(width: 1)
.overlay(
VStack(spacing: 0) {
Spacer()
RoundedRectangle(cornerRadius: 50)
.fill(Color("mode").opacity(0.3))
.frame(width: handleWidth, height: handleHeight)
.onHover { inside in
if inside {
NSCursor.resizeLeftRight.push()
.foregroundStyle(Color("mode").opacity(0.0))
.frame(width: 10)
.onHover { inside in
if inside {
NSCursor.resizeLeftRight.push()
} else {
NSCursor.pop()
} else {
NSCursor.pop()
}
}
.contextMenu {
Button("Reset Size") {
dimension = 280
}
}
.gesture(drag)
Spacer()
}
.offset(x: 5)
)
}
.contextMenu {
Button("Reset Size") {
dimension = 280
}
}
.gesture(drag)
.help("Right click to reset size")
}
var drag: some Gesture {
@@ -302,13 +269,13 @@ public struct SlideableDivider: View {
let maxWidth: Double = 350
dimension = max(minWidth, min(maxWidth, newDimension))
NSCursor.closedHand.set()
handleWidth = 4
handleWidth = 6
handleHeight = 40
}
.onEnded { val in
dimensionStart = nil
NSCursor.arrow.set()
handleWidth = 2
handleWidth = 4
handleHeight = 30
}
}
@@ -323,18 +290,18 @@ struct LabeledDivider: View {
HStack(spacing: 0) {
Rectangle()
.frame(height: 1)
.foregroundColor(.gray.opacity(0.2))
.foregroundColor(Color("mode").opacity(0.2))
Text(label)
.textCase(.uppercase)
.font(.title2)
.foregroundColor(.gray.opacity(0.6))
.foregroundColor(Color("mode").opacity(0.6))
.padding(.horizontal, 10)
.frame(minWidth: 80)
Rectangle()
.frame(height: 1)
.foregroundColor(.gray.opacity(0.2))
.foregroundColor(Color("mode").opacity(0.2))
}
.frame(minHeight: 35)
}
@@ -497,7 +464,7 @@ struct SimpleSearchStyle: TextFieldStyle {
}
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color.gray.opacity(0.2), lineWidth: 1)
.strokeBorder(Color("mode").opacity(0.2), lineWidth: 1)
.allowsHitTesting(false)
}
}
@@ -767,7 +734,7 @@ struct PillPicker: View {
Text(i == 0 ? "Apps" : (i == 1 ? "Widgets" : "Plugins"))
.font(.system(size: 12))
.foregroundColor(index == i ? (colorScheme == .dark ? .black : Color("AccentColor")) : .gray)
.foregroundColor(index == i ? (colorScheme == .dark ? .black : Color("AccentColor")) : Color("mode"))
}
.padding(5)
.frame(height: 25)
@@ -826,3 +793,47 @@ struct PearDropView: View {
}
}
// Used for testing UI bounds
extension View {
func bounds() -> some View {
self.border(Color.red, width: 1)
}
}
@ViewBuilder
func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: Bool = false) -> some View {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
darker ? themeSettings.themeColor.darker(by: 5).edgesIgnoringSafeArea(.all) : themeSettings.themeColor.edgesIgnoringSafeArea(.all)
}
}
struct PresetColor: ButtonStyle {
var fillColor: Color
var label: String
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center, spacing: 10) {
Circle()
.frame(width: 20, height: 20)
.fixedSize(horizontal: true, vertical: true)
.foregroundColor(fillColor)
.background(fillColor)
.clipShape(Circle())
.help(label)
.overlay(
RoundedRectangle(cornerRadius: 25)
.strokeBorder(Color("mode").opacity(0.8), lineWidth: 1.5))
// Text(label)
}
.onHover { inside in inside ? NSCursor.pointingHand.push() : NSCursor.pop() }
}
}
+59
View File
@@ -0,0 +1,59 @@
//
// ThemeManager.swift
// Pearcleaner
//
// Created by Alin Lupascu on 4/12/24.
//
import Foundation
import SwiftUI
import AppKit
class ThemeSettings: ObservableObject {
static let shared = ThemeSettings()
private let userDefaults = UserDefaults.standard
private let colorKey = "themeColor"
@Published var themeColor: Color {
didSet {
saveThemeColor()
}
}
// light - Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
// dark - Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1)
init() {
// Initialize color from UserDefaults or use a default value
if let components = UserDefaults.standard.object(forKey: colorKey) as? [CGFloat], components.count >= 4 {
themeColor = Color(.sRGB, red: components[0], green: components[1], blue: components[2], opacity: components[3])
} else {
themeColor = Color.clear
}
}
func saveThemeColor() {
let nsColor = NSColor(themeColor)
if let components = nsColor.cgColor.components {
UserDefaults.standard.set(components, forKey: colorKey)
}
}
func resetToDefault(dark: Bool = true) {
themeColor = dark ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
saveThemeColor()
}
}
extension Color {
func darker(by percentage: CGFloat = 30.0) -> Color {
var hsb = (hue: CGFloat(0), saturation: CGFloat(0), brightness: CGFloat(0), alpha: CGFloat(0))
NSColor(self).getHue(&hsb.hue, saturation: &hsb.saturation, brightness: &hsb.brightness, alpha: &hsb.alpha)
return Color(hue: hsb.hue, saturation: hsb.saturation, brightness: max(hsb.brightness - percentage/100, 0), opacity: hsb.alpha)
}
func lighter(by percentage: CGFloat = 30.0) -> Color {
var hsb = (hue: CGFloat(0), saturation: CGFloat(0), brightness: CGFloat(0), alpha: CGFloat(0))
NSColor(self).getHue(&hsb.hue, saturation: &hsb.saturation, brightness: &hsb.brightness, alpha: &hsb.alpha)
return Color(hue: hsb.hue, saturation: hsb.saturation, brightness: min(hsb.brightness + percentage / 100, 1.0), opacity: hsb.alpha)
}
}
+4
View File
@@ -59,6 +59,7 @@ struct PearcleanerApp: App {
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
.onOpenURL(perform: { url in
@@ -114,6 +115,7 @@ struct PearcleanerApp: App {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}, icon: selectedMenubarIcon)
}
@@ -170,6 +172,7 @@ struct PearcleanerApp: App {
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.toolbarBackground(.clear)
.preferredColorScheme(displayMode.colorScheme)
}
@@ -192,6 +195,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
findAndHideWindows(named: ["Pearcleaner"])
NSApplication.shared.setActivationPolicy(.accessory)
}
}
+4
View File
@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
<string>$(APP_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(APP_BUILD)</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
+8 -8
View File
@@ -53,14 +53,14 @@ struct AboutSettingsTab: View {
Text("Microsoft Designer")
Text("Application icon resource")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://designer.microsoft.com/image-creator")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -78,14 +78,14 @@ struct AboutSettingsTab: View {
Text("Namelix")
Text("Logo and branding generation")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://namelix.com/")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -103,7 +103,7 @@ struct AboutSettingsTab: View {
Text("Privacy Guides")
Text("Inspired by open-source appcleaner script from Sun Knudsen")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
.lineLimit(2)
}
@@ -112,7 +112,7 @@ struct AboutSettingsTab: View {
Button("") {
NSWorkspace.shared.open(URL(string: "https://sunknudsen.com/privacy-guides/how-to-clean-uninstall-macos-apps-using-appcleaner-open-source-alternative")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -130,14 +130,14 @@ struct AboutSettingsTab: View {
Text("AppCleaner")
Text("Inspired by AppCleaner from Freemacsoft")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://freemacsoft.net/appcleaner/")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
+53 -35
View File
@@ -8,6 +8,7 @@
import Foundation
import SwiftUI
import ServiceManagement
import FinderSync
struct GeneralSettingsTab: View {
@EnvironmentObject var appState: AppState
@@ -25,7 +26,7 @@ struct GeneralSettingsTab: View {
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
// @State private var finderExtensionEnabled: Bool = false
@State private var diskStatus: Bool = false
@State private var accessStatus: Bool = false
private let themes = ["Auto", "Dark", "Light"]
@@ -49,11 +50,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(instantSearch ? "Instant search is enabled" : "Instant search is disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When instant search is enabled, all application files are gathered and cached for later use on startup instead of on each app click. There might be a slight delay of a few seconds when launching Pearcleaner depending on the amount of apps you have installed.", color: nil, label: "")
@@ -73,11 +74,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(brew ? "Homebrew cleanup is enabled" : "Homebrew cleanup is disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force", color: nil, label: "")
@@ -97,11 +98,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("File list sorting mode")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
Spacer()
@@ -114,28 +115,6 @@ struct GeneralSettingsTab: View {
}
.padding(5)
.padding(.leading)
// HStack(spacing: 0) {
// Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
// .resizable()
// .scaledToFit()
// .frame(width: 20, height: 20)
// .padding(.trailing)
// .foregroundStyle(.gray)
// VStack(alignment: .leading, spacing: 5) {
// Text("\(selectedSortAlpha ? "File list sorted alphabetically" : "File list sorted by size")")
// .font(.callout)
// .foregroundStyle(.gray)
// }
//
// InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
//
// Spacer()
// Toggle(isOn: $selectedSortAlpha, label: {
// })
// .toggleStyle(.switch)
// }
// .padding(5)
// .padding(.leading)
// === Perms ================================================================================================
@@ -163,7 +142,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission **NOT** granted")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Button("") {
@@ -171,7 +150,7 @@ struct GeneralSettingsTab: View {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane"))
}
.padding(5)
@@ -188,7 +167,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission **NOT** granted")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Button("") {
@@ -196,7 +175,7 @@ struct GeneralSettingsTab: View {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane"))
}
.padding(5)
@@ -224,7 +203,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Toggle(isOn: $sentinel, label: {
@@ -244,6 +223,44 @@ struct GeneralSettingsTab: View {
// === Finder Extension =============================================================================================
Divider()
.padding()
HStack() {
Text("Finder Extension").font(.title2)
Spacer()
}
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: appState.finderExtensionEnabled ? "puzzlepiece.extension.fill" : "puzzlepiece.extension")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(appState.finderExtensionEnabled ? .green : .red)
Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner", color: nil, label: "")
Spacer()
Button("Extensions") {
FIFinderSyncController.showExtensionManagementInterface()
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "Show Extensions Pane"))
}
.padding(5)
.padding(.leading)
@@ -253,11 +270,12 @@ struct GeneralSettingsTab: View {
.onAppear {
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
appState.updateExtensionStatus()
}
}
.padding(20)
.frame(width: 500, height: 460)
.frame(width: 500, height: 560)
}
+94 -49
View File
@@ -14,7 +14,9 @@ struct InterfaceSettingsTab: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations
@EnvironmentObject var fsm: FolderSettingsManager
@EnvironmentObject var themeSettings: ThemeSettings
@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
@@ -31,7 +33,16 @@ struct InterfaceSettingsTab: View {
let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"]
private let themes = ["Auto", "Dark", "Light"]
// @State private var slate = Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
// @State private var solarized = Color(.sRGB, red: 0.117257, green: 0.22506, blue: 0.249171, opacity: 1)
// @State private var dracula = Color(.sRGB, red: 0.268614, green: 0.264737, blue: 0.383503, opacity: 1)
var body: some View {
let slate = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.499549, green: 0.545169, blue: 0.682028, opacity: 1) : Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
let solarized = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.554372, green: 0.6557, blue: 0.734336, opacity: 1) : Color(.sRGB, red: 0.117257, green: 0.22506, blue: 0.249171, opacity: 1)
let dracula = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.567094, green: 0.562125, blue: 0.81285, opacity: 1) : Color(.sRGB, red: 0.268614, green: 0.264737, blue: 0.383503, opacity: 1)
Form {
VStack {
@@ -47,11 +58,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(glass ? "Transparent material enabled" : "Transparent material disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Toggle(isOn: $glass, label: {
@@ -65,6 +76,57 @@ struct InterfaceSettingsTab: View {
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: "paintbrush")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Application base color")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When using a custom color, you might need to change the application color mode below to Dark or Light so text is readable", color: nil, label: "")
Spacer()
Button("") {
themeSettings.themeColor = slate
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: slate, label: "Slate"))
Button("") {
themeSettings.themeColor = dracula
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: dracula, label: "Dracula"))
.padding(.horizontal)
Button("") {
themeSettings.themeColor = solarized
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: solarized, label: "Solarized"))
Spacer()
ColorPicker("", selection: $themeSettings.themeColor, supportsOpacity: false)
.onChange(of: themeSettings.themeColor) { newValue in
themeSettings.saveThemeColor()
}
.padding(.horizontal, 5)
Button("") {
themeSettings.resetToDefault(dark: colorScheme == .dark)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reset color to default"))
}
.padding(5)
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: {
@@ -81,12 +143,13 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Application color mode")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "Changing the color mode will reset the base color to defaults", color: nil, label: "")
Spacer()
Picker("", selection: $selectedTheme) {
ForEach(themes, id: \.self) { theme in
@@ -99,6 +162,7 @@ struct InterfaceSettingsTab: View {
switch newTheme {
case "Auto":
displayMode.colorScheme = nil
themeSettings.resetToDefault(dark: isDarkModeEnabled())
// Refresh foreground colors
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
@@ -108,11 +172,19 @@ struct InterfaceSettingsTab: View {
}
case "Dark":
displayMode.colorScheme = .dark
themeSettings.resetToDefault(dark: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
}
if menubarEnabled{
MenuBarExtraManager.shared.restartMenuBarExtra()
}
case "Light":
displayMode.colorScheme = .light
themeSettings.resetToDefault(dark: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
}
if menubarEnabled{
MenuBarExtraManager.shared.restartMenuBarExtra()
}
@@ -145,11 +217,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(mini ? "Mini window mode" : "Full size window mode")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Toggle(isOn: $mini, label: {
@@ -167,6 +239,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
updateOnMain(after: 0.1, {
@@ -184,6 +257,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
updateOnMain(after: 0.1, {
@@ -204,11 +278,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(miniView ? "Show apps list on startup" : "Show drop target on startup")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil, label: "")
@@ -231,11 +305,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(popoverStay ? "Popover window will stay on top" : "Popover window will not stay on top")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.", color: nil, label: "")
@@ -267,11 +341,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(menubarEnabled ? "Menubar icon enabled" : "Menubar icon disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.", color: nil, label: "")
Spacer()
@@ -285,6 +359,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}, icon: selectedMenubarIcon)
NSApplication.shared.setActivationPolicy(.accessory)
@@ -300,6 +375,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner")
@@ -309,6 +385,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner")
@@ -329,11 +406,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(isLaunchAtLoginEnabled ? "Launch at login enabled" : "Launch at login disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.", color: nil, label: "")
Spacer()
@@ -370,11 +447,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Menubar icon")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Picker("", selection: $selectedMenubarIcon) {
@@ -403,44 +480,12 @@ struct InterfaceSettingsTab: View {
.padding(5)
.padding(.leading)
// HStack(spacing: 0) {
// Image(systemName: "dock.rectangle")
// .resizable()
// .scaledToFit()
// .frame(width: 20, height: 20)
// .padding(.trailing)
// .foregroundStyle(.gray)
// VStack(alignment: .leading, spacing: 5) {
// Text("\(dockEnabled ? "Show dock icon" : "Hide dock icon")")
// .font(.callout)
// .foregroundStyle(.gray)
// }
// InfoButton(text: "This setting only affects Pearcleaner when the menubar icon is enabled, otherwise the dock icon will always show", color: nil, label: "")
// Spacer()
// Toggle(isOn: $dockEnabled, label: {
// })
// .toggleStyle(.switch)
// .onChange(of: dockEnabled) { newValue in
// if newValue {
// NSApplication.shared.setActivationPolicy(.regular)
// } else {
// if menubarEnabled {
// NSApplication.shared.setActivationPolicy(.accessory)
// }
//
// }
// }
//
// }
// .padding(5)
// .padding(.leading)
Spacer()
}
}
.padding(20)
.frame(width: 500, height: 520)
.frame(width: 500, height: 550)
}
+3 -1
View File
@@ -10,6 +10,7 @@ import SwiftUI
struct SettingsView: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var fsm: FolderSettingsManager
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var showPopover: Bool
@Binding var search: String
@Binding var showFeature: Bool
@@ -49,7 +50,8 @@ struct SettingsView: View {
}
.tag(CurrentTabView.about)
}
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.background(backgroundView(themeSettings: themeSettings, glass: glass))
// .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
}
+5 -5
View File
@@ -66,7 +66,7 @@ struct UpdateSettingsTab: View {
Text("Showing last 3 releases")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
@@ -76,28 +76,28 @@ struct UpdateSettingsTab: View {
Button(""){
loadGithubReleases(appState: appState)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reload release notes", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reload release notes"))
Spacer()
Button(""){
showFeature.toggle()
}
.buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert"))
Spacer()
Button(""){
loadGithubReleases(appState: appState, manual: true)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.square", help: "Check for updates", color: Color("mode")))
.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", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View releases on GitHub"))
Spacer()
}
+52 -24
View File
@@ -10,13 +10,14 @@ import SwiftUI
struct AppListItems: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var search: String
@State private var isHovered = false
@State private var windowSettings = WindowSettings()
@Environment(\.colorScheme) var colorScheme
// @AppStorage("settings.general.mini") private var mini: Bool = false
// @AppStorage("settings.general.popover") private var popoverStay: Bool = true
@AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.mini") private var mini: Bool = false
@Binding var showPopover: Bool
@EnvironmentObject var locations: Locations
let itemId = UUID()
@@ -24,15 +25,12 @@ struct AppListItems: View {
var isSelected: Bool {
appState.appInfo.path == appInfo.path
}
@State private var hoveredItemPath: URL? = nil
var body: some View {
// VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .center) {
if isHovered || isSelected {
if (isHovered || isSelected) && mini {
RoundedRectangle(cornerRadius: 50)
.fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 4 : 2, height: 25)
@@ -41,24 +39,12 @@ struct AppListItems: View {
if let appIcon = appInfo.appIcon {
ZStack {
// RoundedRectangle(cornerRadius: 8)
// .fill(Color(appIcon.averageColor!))
// .frame(width: 35, height: 35)
// .saturation(3)
// .opacity(0.5)
//// .brightness(displayMode.colorScheme == .dark ? 0 : 0.5)
//// .shadow(color: .black, radius: 1, y: 2)
// RoundedRectangle(cornerRadius: 8)
// .strokeBorder(Color("mode").opacity(0.1), lineWidth: 1)
// .frame(width: 35, height: 35)
Image(nsImage: appIcon)
.resizable()
.aspectRatio(contentMode: .fit)
// .scaledToFit()
.frame(width: 30, height: 30)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(color: .black.opacity(0.5), radius: 2, y: 2)
// .grayscale(opacityForItem())
}
}
@@ -100,31 +86,73 @@ struct AppListItems: View {
.font(.footnote)
.foregroundStyle(Color("mode").opacity(0.5))
if (isHovered || isSelected) && !mini {
Triangle()
.fill(isSelected ? themeSettings.themeColor : Color("mode").opacity(0.1))
.frame(width: isSelected ? 16 : 8, height: 30)
.padding(.leading, 5)
.offset(x: 22)
.zIndex(5)
}
}
.padding(.horizontal, 5)
.help(appInfo.appName)
.onHover { hovering in
withAnimation(Animation.easeIn(duration: 0.2)) {
self.isHovered = hovering
self.hoveredItemPath = isHovered ? appInfo.path : nil
}
}
.onTapGesture {
withAnimation(Animation.easeInOut(duration: 0.4)) {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
if isSelected {
appState.appInfo = .empty
appState.currentView = miniView ? .apps : .empty
showPopover = false
} else {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
}
}
}
// .grayscale(opacityForItem(appInfo.path))
}
func opacityForItem() -> Double {
func opacityForItem(_ path: URL) -> Double {
// Check if any item is selected
let isAnyItemSelected = appState.sortedApps.contains(where: { $0.path == appState.appInfo.path })
// If this item is selected or no items are selected, keep full opacity
// Otherwise, reduce opacity
return isAnyItemSelected ? (appState.appInfo.path == appInfo.path ? 0 : 1.0) : 0
let isItemSelected = appState.appInfo.path == path
let isItemHovered = hoveredItemPath == path
// Logic to determine grayscale level
if isItemSelected || !isAnyItemSelected || isItemHovered {
return 0 // No grayscale
} else {
return 1.0 // Full grayscale
}
// return isAnyItemSelected ? (appState.appInfo.path == appInfo.path ? 0 : 1.0) : 0
}
}
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
// Start at the top right
path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
// Add line to the bottom right
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
// Add line to the left point
path.addLine(to: CGPoint(x: rect.minX, y: rect.midY))
// Close the path
path.closeSubpath()
return path
}
}
+4
View File
@@ -11,6 +11,8 @@ import SwiftUI
struct AppsListView: View {
@Binding var search: String
@Binding var showPopover: Bool
@AppStorage("settings.general.mini") private var mini: Bool = false
var filteredApps: [AppInfo]
var body: some View {
@@ -23,11 +25,13 @@ struct AppsListView: View {
SectionView(title: "User", count: filteredUserApps.count, apps: filteredUserApps, search: $search, showPopover: $showPopover)
}
if !filteredSystemApps.isEmpty {
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
}
}
.padding(.horizontal)
.padding(.top, !mini ? 4 : 0)
}
.scrollIndicators(.never)
}
+20 -24
View File
@@ -38,23 +38,17 @@ struct FilesView: View {
VStack {
Spacer()
Text("Searching the file system").font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle((Color("mode").opacity(0.5)))
HStack {
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.caption)
.foregroundStyle((.gray.opacity(0.8)))
.opacity(elapsedTime == 0 ? 0 : 1)
// Image(systemName: "\(elapsedTime).circle")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 16, height: 16)
// .foregroundStyle((.gray.opacity(0.8)))
// .opacity(elapsedTime == 0 ? 0 : 1)
}
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.title).monospacedDigit()
.foregroundStyle((Color("mode").opacity(0.5)))
.opacity(elapsedTime == 0 ? 0 : 1)
.contentTransition(.numericText())
Spacer()
}
@@ -62,7 +56,9 @@ struct FilesView: View {
.transition(.opacity)
.onAppear {
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.elapsedTime += 1
withAnimation {
self.elapsedTime += 1
}
}
}
.onDisappear {
@@ -89,7 +85,7 @@ struct FilesView: View {
pathFinder.findPaths()
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
}
@@ -101,7 +97,7 @@ struct FilesView: View {
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
@@ -130,7 +126,7 @@ struct FilesView: View {
VStack(alignment: .leading, spacing: 5){
HStack {
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold)
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold).lineLimit(1)
Text("").foregroundStyle(Color("AccentColor"))
Text("\(appState.appInfo.appVersion)").font(.title3)
if appState.appInfo.appName.count < 5 {
@@ -141,7 +137,7 @@ struct FilesView: View {
Text("\(appState.appInfo.bundleIdentifier)")
.lineLimit(1)
.font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle((Color("mode").opacity(0.5)))
}
.padding(.leading)
@@ -150,7 +146,7 @@ struct FilesView: View {
VStack(alignment: .trailing, spacing: 5) {
Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold)
Text("\(appState.appInfo.fileSize.count == 1 ? "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) item" : "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) items")")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle((Color("mode").opacity(0.5)))
}
}
@@ -218,7 +214,7 @@ struct FilesView: View {
selectedSortAlpha.toggle()
// selectedOption = selectedOption == "Alpha" ? "Size" : "Alpha"
}
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
}
.padding()
@@ -407,7 +403,7 @@ struct FileDetailsItem: View {
Button("") {
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
}
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder"))
}
.background(
+51 -44
View File
@@ -12,7 +12,7 @@ struct MenuBarMiniAppView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations
// @State private var windowSettings = WindowSettings()
@EnvironmentObject var themeSettings: ThemeSettings
@State private var animateGradient: Bool = false
@Binding var search: String
@State private var showSys: Bool = true
@@ -55,7 +55,8 @@ struct MenuBarMiniAppView: View {
} else {
VStack(alignment: .center) {
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(0)
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(.top, 8)
HStack(spacing: 10) {
Button("Leftover Files") {
@@ -84,7 +85,7 @@ struct MenuBarMiniAppView: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
.padding(.leading, 10)
SearchBarMiniBottom(search: $search)
@@ -94,7 +95,7 @@ struct MenuBarMiniAppView: View {
self.showMenu.toggle()
}
.padding(.trailing, 10)
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More"))
.popover(isPresented: $showMenu) {
VStack(alignment: .leading) {
@@ -102,34 +103,36 @@ struct MenuBarMiniAppView: View {
SettingsLink{
Label("Settings", systemImage: "gear")
}
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings"))
} else {
Button("Settings") {
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: NSApp.delegate, from: nil)
showMenu = false
}
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings"))
}
Button("Quit") {
NSApp.terminate(nil)
}
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner"))
}
.padding()
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
}
@@ -139,7 +142,7 @@ struct MenuBarMiniAppView: View {
// .padding(.horizontal)
}
.padding(.vertical, 5)
.padding(.vertical, 8)
.padding(.bottom, 8)
}
@@ -148,19 +151,21 @@ struct MenuBarMiniAppView: View {
}
.frame(minWidth: 300, minHeight: 370)
.edgesIgnoringSafeArea(.all)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
.transition(.opacity)
.popover(isPresented: $showPopover, arrowEdge: .leading) {
VStack {
@@ -174,19 +179,21 @@ struct MenuBarMiniAppView: View {
}
.interactiveDismissDisabled(popoverStay)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
.frame(width: 650, height: 500)
}
+30 -30
View File
@@ -11,6 +11,7 @@ import SwiftUI
struct MiniMode: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var search: String
@State private var showSys: Bool = true
@State private var showUsr: Bool = true
@@ -49,20 +50,21 @@ struct MiniMode: View {
}
.interactiveDismissDisabled(popoverStay)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
}
}
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
)
// .background(Color("pop"))
// .background(backgroundView(themeSettings: themeSettings, glass: false).padding(-80))
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
// .background(
// Group {
// if glass {
// backgroundView(themeSettings: themeSettings).padding(-80)
//// GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
// }
// }
// )
.frame(width: 650, height: 550)
}
@@ -71,7 +73,8 @@ struct MiniMode: View {
}
.frame(minWidth: 300, minHeight: 345)
.edgesIgnoringSafeArea(.all)
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.background(backgroundView(themeSettings: themeSettings, glass: glass))
// .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
// MARK: Background for whole app
// .background(Color("bg").opacity(1))
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
@@ -93,22 +96,20 @@ struct MiniEmptyView: View {
@Binding var showPopover: Bool
var body: some View {
VStack() {
VStack(alignment: .center) {
Spacer()
ZStack {
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(systemName: "plus.square.dashed")
.resizable()
.scaledToFit()
.frame(width: 120, height: 120)
.padding()
.fontWeight(.ultraLight)
)
.frame(width: 160)
}
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 your app here")
.font(.title3)
@@ -133,7 +134,6 @@ struct MiniEmptyView: View {
}
}
}
Spacer()
}
Spacer()
+48 -75
View File
@@ -10,6 +10,7 @@ import SwiftUI
struct RegularMode: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@@ -69,21 +70,16 @@ struct RegularMode: View {
}
.frame(width: sidebarWidth)
.padding(.vertical)
// .onHover { hovering in
// withAnimation(Animation.easeInOut(duration: 0.4)) {
// isHovering = hovering
// }
// }
// .opacity(!isHovering ? 1 : 0.5)
}
}
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.background(backgroundView(themeSettings: themeSettings, darker: true, glass: glass))
.transition(.opacity)
SlideableDivider(dimension: $sidebarWidth)
.background(backgroundView(themeSettings: themeSettings))
.zIndex(3)
// Details View
@@ -104,30 +100,11 @@ struct RegularMode: View {
}
.transition(.opacity)
}
// .background(slate)
// .onHover { hovering in
// withAnimation(Animation.easeInOut(duration: 0.4)) {
// isHovering = hovering
// }
// }
// .opacity((isHovering2 || (!isHovering && !isHovering2)) ? 1 : 0.5)
.zIndex(2)
.background(backgroundView(themeSettings: themeSettings))
}
.frame(minWidth: 900, minHeight: 600)
.edgesIgnoringSafeArea(.all)
// MARK: Background for whole app
// .background{
// Image("peakpx")
// .resizable()
// .scaledToFill()
// .edgesIgnoringSafeArea(.all)
// GlassEffect(material: .sidebar, blendingMode: .withinWindow)
// .edgesIgnoringSafeArea(.all)
// }
// .background(Color("bg").opacity(1))
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
}
}
@@ -188,56 +165,52 @@ struct Header: View {
var body: some View {
HStack {
Text(title).foregroundStyle(Color("mode")).opacity(0.5)
Text("\(title)").foregroundStyle(Color("mode")).opacity(0.5)
HStack {
if hovered {
withAnimation() {
Image(systemName: "arrow.circlepath")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 19, height: 19)
.onTapGesture {
withAnimation {
// Refresh Apps list
appState.reload.toggle()
showPopover = false
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps = sortedApps
if instantSearch {
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
} else {
appState.reload.toggle()
}
}
}
}
.help("Refresh apps")
}
} else {
Text("\(count)")
.font(.system(size: 10))
.frame(minWidth: count > 99 ? 30 : 20, minHeight: 15)
.padding(2)
.background(Color("mode").opacity(0.1))
.clipShape(.capsule)
}
}
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
Text("\(count)")
.font(.system(size: 10))
.monospacedDigit()
.frame(minWidth: count > 99 ? 30 : 24, minHeight: 17)
.background(Color("mode").opacity(0.1))
.clipShape(.capsule)
.padding(.leading, 2)
Spacer()
if hovered {
Text("REFRESH")
.font(.system(size: 10))
.monospaced()
.foregroundStyle(Color("mode").opacity(0.8))
}
}
.padding(.vertical, 9)
.padding(.leading, 6)
// .background(!glass ? Color("windowBG") : .clear)
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
.onTapGesture {
withAnimation {
// Refresh Apps list
appState.reload.toggle()
showPopover = false
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
appState.sortedApps = sortedApps
if instantSearch {
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
} else {
appState.reload.toggle()
}
}
}
}
.frame(minHeight: 20)
.help("Click header or ⌘+R to refresh apps list")
.padding(5)
}
}
+4 -9
View File
@@ -50,13 +50,12 @@ struct TopBar: View {
if instantSearch {
let reverse = ReversePathsSearcher(appState: appState, locations: locations)
reverse.reversePathsSearch()
// reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
}
if appState.currentView != .zombie {
@@ -84,7 +83,7 @@ struct TopBar: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
}
@@ -95,19 +94,15 @@ struct TopBar: View {
appState.appInfo = AppInfo.empty
}
}
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
}
}
}
.padding(.horizontal, 10)
.padding(.top, 15)
.padding(6)
}
}
+4 -4
View File
@@ -51,14 +51,14 @@ struct TopBarMini: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
Button("") {
withAnimation(.easeInOut(duration: 0.5)) {
appState.currentView = .apps
}
}
.buttonStyle(SimpleButtonStyle(icon: "square.grid.3x3.square", help: "Apps List", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "square.grid.3x3.square", help: "Apps List"))
}
@@ -93,7 +93,7 @@ struct TopBarMini: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
Button("") {
withAnimation(.easeInOut(duration: 0.5)) {
@@ -102,7 +102,7 @@ struct TopBarMini: View {
showPopover = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
}
}
+19 -23
View File
@@ -21,7 +21,6 @@ struct ZombieView: View {
@Binding var showPopover: Bool
@Binding var search: String
@State private var searchZ: String = ""
// @State private var selectedOption = "Alpha"
var regularWin: Bool
@State private var elapsedTime = 0
@State private var timer: Timer? = nil
@@ -57,21 +56,17 @@ struct ZombieView: View {
Spacer()
Text("Searching the file system").font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle(Color("mode").opacity(0.5))
HStack {
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.caption)
.foregroundStyle((.gray.opacity(0.8)))
// Image(systemName: "\(elapsedTime).circle")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 16, height: 16)
// .foregroundStyle((.gray.opacity(0.8)))
}
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.title).monospacedDigit()
.foregroundStyle(Color("mode").opacity(0.5))
.opacity(elapsedTime == 0 ? 0 : 1)
.contentTransition(.numericText())
Spacer()
@@ -82,7 +77,9 @@ struct ZombieView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.elapsedTime += 1
withAnimation {
self.elapsedTime += 1
}
}
}
.onDisappear {
@@ -109,7 +106,7 @@ struct ZombieView: View {
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Button("Close") {
updateOnMain {
@@ -119,7 +116,7 @@ struct ZombieView: View {
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
@@ -146,7 +143,7 @@ struct ZombieView: View {
}
Text("Files and folders remaining from previously installed applications")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
@@ -155,7 +152,7 @@ struct ZombieView: View {
Text("\(formatByte(size: filteredAndSortedFiles.1))").font(.title).fontWeight(.bold)
Text("\(appState.zombieFile.fileSize.count == 1 ? "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) item" : "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) items")")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle(Color("mode").opacity(0.5))
}
}
@@ -184,7 +181,7 @@ struct ZombieView: View {
Button("") {
selectedSortAlpha.toggle()
}
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
}
@@ -311,7 +308,6 @@ struct ZombieFileDetailsItem: View {
.lineLimit(1)
.truncationMode(.tail)
.opacity(0.5)
// .foregroundStyle(Color("AccentColor"))
.help(path.path)
}
@@ -324,7 +320,7 @@ struct ZombieFileDetailsItem: View {
Button("") {
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
}
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder"))
}
.background(
+6 -6
View File
@@ -38,10 +38,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Text("Full Disk Access permission to find and delete files in system paths")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
HStack(alignment: .top, spacing: 20) {
@@ -49,10 +49,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Text("Accessibility permission to delete files via Finder")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
HStack(alignment: .top, spacing: 20) {
@@ -60,10 +60,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").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(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
}
.padding(.horizontal, 20)
+1
View File
@@ -1,4 +1,5 @@
{
"3.5.0": "- Finder Extension: When enabled, you can right click an app in finder and uninstall with Pearcleaner directly|- Theme System: You can now select a base theme color for the application window",
"3.3.0": "- Folder Settings: Add more directories where Pearcleaner should search for app files|- Progress: Show progress bar on startup when loading all app files with instant search enabled|- App Icon: Show background color behind app icons in FilesView based on icon's average color mapping|- Progress: Show time counter on regular file search views",
"3.2.0": "- Menubar Item: You can choose to access Pearcleaner from the menubar. This will show a slightly modified Mini Mode view.",
"3.1.0": "- Homebrew Cleanup: If you install apps using brew, you can now enable homebrew cleanup in the settings to have Pearcleaner alert Homebrew that the app was removed externally from Homebrew and keep the brew app list synced.",