mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 06:09:01 +00:00
v3.8.6
This commit is contained in:
@@ -561,8 +561,8 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUILD = 59;
|
APP_BUILD = 60;
|
||||||
APP_VERSION = 3.8.5;
|
APP_VERSION = 3.8.6;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
@@ -631,8 +631,8 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUILD = 59;
|
APP_BUILD = 60;
|
||||||
APP_VERSION = 3.8.5;
|
APP_VERSION = 3.8.6;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
|||||||
@@ -109,10 +109,20 @@ struct AppCommands: Commands {
|
|||||||
saveURLsToFile(urls: appState.selectedItems, appState: appState)
|
saveURLsToFile(urls: appState.selectedItems, appState: appState)
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Export Files", systemImage: "square.and.arrow.up")
|
Label("Export File Paths", systemImage: "square.and.arrow.up")
|
||||||
}
|
}
|
||||||
.keyboardShortcut("e", modifiers: .command)
|
.keyboardShortcut("e", modifiers: .command)
|
||||||
|
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
if !appState.appInfo.bundleIdentifier.isEmpty {
|
||||||
|
saveURLsToFile(urls: appState.selectedItems, appState: appState, copy: true)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Copy File Paths", systemImage: "square.and.arrow.up")
|
||||||
|
}
|
||||||
|
.keyboardShortcut("c", modifiers: [.command, .option])
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ class AppPathFinder {
|
|||||||
let bundleComponents = self.appInfo.bundleIdentifier.components(separatedBy: ".").compactMap { $0 != "-" ? $0.lowercased() : nil }
|
let bundleComponents = self.appInfo.bundleIdentifier.components(separatedBy: ".").compactMap { $0 != "-" ? $0.lowercased() : nil }
|
||||||
let bundle = bundleComponents.suffix(2).joined()
|
let bundle = bundleComponents.suffix(2).joined()
|
||||||
let nameL = self.appInfo.appName.pearFormat()
|
let nameL = self.appInfo.appName.pearFormat()
|
||||||
|
let nameLFiltered = nameL.filter { $0.isLetter }
|
||||||
|
|
||||||
let nameP = self.appInfo.path.lastPathComponent.replacingOccurrences(of: ".app", with: "")
|
let nameP = self.appInfo.path.lastPathComponent.replacingOccurrences(of: ".app", with: "")
|
||||||
|
|
||||||
for condition in conditions {
|
for condition in conditions {
|
||||||
@@ -178,7 +180,7 @@ class AppPathFinder {
|
|||||||
return itemL.contains(bundleIdentifierL)
|
return itemL.contains(bundleIdentifierL)
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemL.contains(bundleIdentifierL) || itemL.contains(bundle) || (nameL.count > 3 && itemL.contains(nameL)) || (nameP.count > 3 && itemL.contains(nameP))
|
return itemL.contains(bundleIdentifierL) || itemL.contains(bundle) || (nameL.count > 3 && itemL.contains(nameL)) || (nameP.count > 3 && itemL.contains(nameP) || (nameLFiltered.count > 3 && itemL.contains(nameLFiltered)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ let skipConditions: [SkipCondition] = [
|
|||||||
|
|
||||||
|
|
||||||
// Skip files/folders during leftover file search
|
// Skip files/folders during leftover file search
|
||||||
let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "mobiledocuments", "mobile", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders"]
|
let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "mobiledocuments", "mobile", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders", "pbs"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -61,38 +61,58 @@ func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSetting
|
|||||||
// Brew cleanup
|
// Brew cleanup
|
||||||
func caskCleanup(app: String) {
|
func caskCleanup(app: String) {
|
||||||
Task(priority: .high) {
|
Task(priority: .high) {
|
||||||
let process = Process()
|
let formattedApp = app.lowercased().replacingOccurrences(of: " ", with: "-")
|
||||||
|
print(formattedApp)
|
||||||
|
|
||||||
#if arch(x86_64)
|
#if arch(x86_64)
|
||||||
let cmd = "/usr/local/bin/brew"
|
let cmd = "/usr/local/bin/brew"
|
||||||
#elseif arch(arm64)
|
#elseif arch(arm64)
|
||||||
let cmd = "/opt/homebrew/bin/brew"
|
let cmd = "/opt/homebrew/bin/brew"
|
||||||
#endif
|
#endif
|
||||||
let formattedApp = app.lowercased().replacingOccurrences(of: " ", with: "-")
|
|
||||||
process.executableURL = URL(fileURLWithPath: "/bin/zsh")
|
let script = """
|
||||||
// process.arguments = ["-c", "\(cmd) uninstall --cask \"\(formattedApp)\" --force; \(cmd) cleanup"]
|
tell application "Terminal"
|
||||||
process.arguments = ["-c", """
|
activate
|
||||||
found_cask=$(\(cmd) list --cask | grep "\(formattedApp)")
|
do script "
|
||||||
if [ -n "$found_cask" ]; then
|
found_cask=$(\(cmd) list --cask | grep '\(formattedApp)');
|
||||||
\(cmd) uninstall --cask "$found_cask" --force;
|
if [ -n \\"$found_cask\\" ]; then
|
||||||
|
clear;
|
||||||
|
\(cmd) uninstall --cask \\"$found_cask\\" --force;
|
||||||
\(cmd) cleanup;
|
\(cmd) cleanup;
|
||||||
else
|
else
|
||||||
echo "Cask not found for \(formattedApp)";
|
echo \\"Cask not found for \(formattedApp)\\";
|
||||||
fi
|
fi"
|
||||||
"""]
|
end tell
|
||||||
let pipe = Pipe()
|
"""
|
||||||
process.standardOutput = pipe
|
|
||||||
process.standardError = pipe
|
var error: NSDictionary?
|
||||||
try? process.run()
|
if let appleScript = NSAppleScript(source: script) {
|
||||||
process.waitUntilExit() // Ensure the process completes
|
appleScript.executeAndReturnError(&error)
|
||||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
}
|
||||||
let output = (String(data: data, encoding: .utf8) ?? "") as String
|
|
||||||
printOS(output)
|
if let error = error {
|
||||||
|
print("AppleScript Error: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Print list of files locally
|
// Print list of files locally
|
||||||
func saveURLsToFile(urls: Set<URL>, appState: AppState) {
|
func saveURLsToFile(urls: Set<URL>, appState: AppState, copy: Bool = false) {
|
||||||
|
|
||||||
|
if copy {
|
||||||
|
let homeDirectory = FileManager.default.homeDirectoryForCurrentUser.path
|
||||||
|
var fileContent = ""
|
||||||
|
let sortedUrls = urls.sorted { $0.path < $1.path }
|
||||||
|
|
||||||
|
for url in sortedUrls {
|
||||||
|
let pathWithTilde = url.path.replacingOccurrences(of: homeDirectory, with: "~")
|
||||||
|
fileContent += "\(pathWithTilde)\n"
|
||||||
|
}
|
||||||
|
copyToClipboard(fileContent)
|
||||||
|
} else {
|
||||||
let panel = NSOpenPanel()
|
let panel = NSOpenPanel()
|
||||||
panel.canChooseFiles = false
|
panel.canChooseFiles = false
|
||||||
panel.canChooseDirectories = true
|
panel.canChooseDirectories = true
|
||||||
@@ -101,13 +121,13 @@ func saveURLsToFile(urls: Set<URL>, appState: AppState) {
|
|||||||
|
|
||||||
if panel.runModal() == .OK, let selectedFolder = panel.url {
|
if panel.runModal() == .OK, let selectedFolder = panel.url {
|
||||||
let filePath = selectedFolder.appendingPathComponent("Export-\(appState.appInfo.appName)(v\(appState.appInfo.appVersion)).txt")
|
let filePath = selectedFolder.appendingPathComponent("Export-\(appState.appInfo.appName)(v\(appState.appInfo.appVersion)).txt")
|
||||||
|
let homeDirectory = FileManager.default.homeDirectoryForCurrentUser.path
|
||||||
var fileContent = ""
|
var fileContent = ""
|
||||||
var count = 1
|
|
||||||
let sortedUrls = urls.sorted { $0.path < $1.path }
|
let sortedUrls = urls.sorted { $0.path < $1.path }
|
||||||
|
|
||||||
for url in sortedUrls {
|
for url in sortedUrls {
|
||||||
fileContent += "[\(count)] - \(url.path)\n"
|
let pathWithTilde = url.path.replacingOccurrences(of: homeDirectory, with: "~")
|
||||||
count += 1
|
fileContent += "\(pathWithTilde)\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -118,12 +138,16 @@ func saveURLsToFile(urls: Set<URL>, appState: AppState) {
|
|||||||
} catch {
|
} catch {
|
||||||
printOS("Error saving file: \(error)")
|
printOS("Error saving file: \(error)")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
printOS("Folder selection was canceled.")
|
printOS("Folder selection was canceled.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Open trash folder
|
// Open trash folder
|
||||||
func openTrash() {
|
func openTrash() {
|
||||||
if let trashURL = try? FileManager.default.url(for: .trashDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
|
if let trashURL = try? FileManager.default.url(for: .trashDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ struct GeneralSettingsTab: View {
|
|||||||
.foregroundStyle(.primary.opacity(0.5))
|
.foregroundStyle(.primary.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")
|
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and launch Terminal.app to execute a brew uninstall and cleanup command to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed. Terminal is required since some apps need sudo permissions to remove services and files placed in system folders.\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")
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
Toggle(isOn: $brew, label: {
|
Toggle(isOn: $brew, label: {
|
||||||
|
|||||||
Reference in New Issue
Block a user