This commit is contained in:
Alin
2024-04-25 16:45:53 -06:00
parent 8f8972084c
commit a7eb573daa
18 changed files with 467 additions and 259 deletions
+22 -18
View File
@@ -521,10 +521,12 @@
C72893142AFD51EA00C8C1CD /* Debug */ = { C72893142AFD51EA00C8C1CD /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_BUILD = "$(APP_BUILD)";
APP_VERSION = "$(APP_VERSION)";
CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements; CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = ""; CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -540,10 +542,12 @@
C72893152AFD51EA00C8C1CD /* Release */ = { C72893152AFD51EA00C8C1CD /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_BUILD = "$(APP_BUILD)";
APP_VERSION = "$(APP_VERSION)";
CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements; CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = ""; CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -560,8 +564,8 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 36; APP_BUILD = 38;
APP_VERSION = 3.5.2; APP_VERSION = 3.5.4;
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;
@@ -593,7 +597,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_ENTITLEMENTS = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 33; CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -616,7 +620,7 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2; MARKETING_VERSION = "$(APP_VERSION)";
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@@ -630,8 +634,8 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 36; APP_BUILD = 38;
APP_VERSION = 3.5.2; APP_VERSION = 3.5.4;
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;
@@ -663,7 +667,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_ENTITLEMENTS = "";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 33; CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@@ -680,7 +684,7 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.4.2; MARKETING_VERSION = "$(APP_VERSION)";
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@@ -692,15 +696,14 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_BUILD = 36; APP_BUILD = "$(APP_BUILD)";
APP_VERSION = 3.5.2; APP_VERSION = "$(APP_VERSION)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
@@ -718,7 +721,6 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -730,15 +732,14 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_BUILD = 36; APP_BUILD = "$(APP_BUILD)";
APP_VERSION = 3.5.2; APP_VERSION = "$(APP_VERSION)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = "$(APP_BUILD)";
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
@@ -756,7 +757,6 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "$(APP_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -767,6 +767,8 @@
C781216D2BC892A000BE06BD /* Debug */ = { C781216D2BC892A000BE06BD /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_BUILD = "$(APP_BUILD)";
APP_VERSION = "$(APP_VERSION)";
CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements; CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
@@ -797,6 +799,8 @@
C781216E2BC892A000BE06BD /* Release */ = { C781216E2BC892A000BE06BD /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_BUILD = "$(APP_BUILD)";
APP_VERSION = "$(APP_VERSION)";
CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements; CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
+4 -19
View File
@@ -177,7 +177,7 @@ class AppPathFinder {
private func getAllContainers(bundleURL: URL) -> [URL] { private func getAllContainers(bundleURL: URL) -> [URL] {
var containers = [URL]() var containers: [URL] = []
// Extract bundle identifier from bundleURL // Extract bundle identifier from bundleURL
let bundleIdentifier = Bundle(url: bundleURL)?.bundleIdentifier let bundleIdentifier = Bundle(url: bundleURL)?.bundleIdentifier
@@ -190,7 +190,9 @@ class AppPathFinder {
// Get the regular container URL for the extracted bundle identifier // Get the regular container URL for the extracted bundle identifier
if let groupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: containerBundleIdentifier) { if let groupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: containerBundleIdentifier) {
containers.append(groupContainer) if FileManager.default.fileExists(atPath: groupContainer.path) {
containers.append(groupContainer)
}
} else { } else {
printOS("Get Containers: Failed to retrieve container URL for bundle identifier: \(containerBundleIdentifier)") printOS("Get Containers: Failed to retrieve container URL for bundle identifier: \(containerBundleIdentifier)")
} }
@@ -221,23 +223,6 @@ class AppPathFinder {
printOS("Error accessing the Containers directory: \(error)") printOS("Error accessing the Containers directory: \(error)")
} }
// let fileURL = URL(fileURLWithPath: "/Users/alin/Library/Containers/2792352D-95FE-43AE-947D-FF4BF31DE4E6")
//
// do {
// // Retrieve the localized name
// let resourceValues = try fileURL.resourceValues(forKeys: [.localizedNameKey])
// if let localizedName = resourceValues.localizedName {
// print("Localized Name: \(localizedName)")
// } else {
// print("Localized name not available.")
// }
// } catch {
// print("Error retrieving localized name: \(error)")
// }
// Return all found containers // Return all found containers
return containers return containers
} }
+4 -3
View File
@@ -24,7 +24,7 @@ class AppState: ObservableObject
@Published var currentView = CurrentDetailsView.empty @Published var currentView = CurrentDetailsView.empty
@Published var showAlert: Bool = false @Published var showAlert: Bool = false
@Published var sidebar: Bool = true @Published var sidebar: Bool = true
@Published var isReminderVisible: Bool = false // @Published var isReminderVisible: Bool = false
@Published var releases = [Release]() @Published var releases = [Release]()
@Published var progressBar: (String, Double) = ("Ready", 0.0) @Published var progressBar: (String, Double) = ("Ready", 0.0)
@Published var reload: Bool = false @Published var reload: Bool = false
@@ -55,6 +55,7 @@ class AppState: ObservableObject
fileSizeLogical: [:], fileSizeLogical: [:],
fileIcon: [:] fileIcon: [:]
) )
updateExtensionStatus() updateExtensionStatus()
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
@@ -66,9 +67,9 @@ class AppState: ObservableObject
} }
@objc func updateExtensionStatus() { @objc func updateExtensionStatus() {
let extentionStatus = FIFinderSyncController.isExtensionEnabled let extensionStatus = FIFinderSyncController.isExtensionEnabled
DispatchQueue.main.async { DispatchQueue.main.async {
self.finderExtensionEnabled = extentionStatus self.finderExtensionEnabled = extensionStatus
} }
} }
} }
+74 -1
View File
@@ -47,6 +47,80 @@ struct SimpleButtonStyle: ButtonStyle {
} }
struct RegularButtonStyle: ButtonStyle {
let icon: String
let label: String
let help: String
@State private var hovered = false
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center) {
Image(systemName: icon).foregroundColor(Color("mode")).padding(.leading, 2).padding(.trailing, 0)
Text(label).textCase(.uppercase).foregroundColor(Color("mode")).fontWeight(.bold).padding(.trailing, 2)
}
.padding(8)
.background(!configuration.isPressed ? hovered ? Color("mode").opacity(0.4) : Color("mode").opacity(0) : Color("mode").opacity(0.5))
.cornerRadius(6)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
.onHover { hovering in
withAnimation(Animation.easeIn(duration: 0.15)) {
hovered = hovering
}
}
.overlay(
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color("mode").opacity(0.5), lineWidth: 1)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
)
.help(help)
}
}
struct ResetSettingsButtonStyle: ButtonStyle {
@Binding var isResetting: Bool
let label: String
let help: String
@State private var hovered = false
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center) {
if isResetting {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.controlSize(.small)
.frame(width: 15)
} else {
Image(systemName: "gear")
.foregroundColor(Color("mode")).padding(.leading, 2).padding(.trailing, 0)
}
Text(label)
.textCase(.uppercase).foregroundColor(Color("mode")).fontWeight(.bold).padding(.trailing, 2)
}
.padding(8)
.background(!configuration.isPressed ? hovered ? Color("mode").opacity(0.4) : Color("mode").opacity(0) : Color("mode").opacity(0.5))
.cornerRadius(6)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
.onHover { hovering in
withAnimation(Animation.easeIn(duration: 0.15)) {
hovered = hovering
}
}
.overlay(
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color("mode").opacity(0.5), lineWidth: 1)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
)
.help(help)
}
}
struct InfoButton: View { struct InfoButton: View {
@State private var isPopoverPresented: Bool = false @State private var isPopoverPresented: Bool = false
@@ -769,7 +843,6 @@ func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: B
} }
struct PresetColor: ButtonStyle { struct PresetColor: ButtonStyle {
var fillColor: Color var fillColor: Color
var label: String var label: String
+6 -2
View File
@@ -22,13 +22,13 @@ class ThemeSettings: ObservableObject {
// light - Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) // 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) // dark - Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1)
// slate - Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
init() { init() {
// Initialize color from UserDefaults or use a default value // Initialize color from UserDefaults or use a default value
if let components = UserDefaults.standard.object(forKey: colorKey) as? [CGFloat], components.count >= 4 { 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]) themeColor = Color(.sRGB, red: components[0], green: components[1], blue: components[2], opacity: components[3])
} else { } else {
themeColor = Color.clear themeColor = Color.clear
} }
} }
@@ -42,7 +42,8 @@ class ThemeSettings: ObservableObject {
} }
func resetToDefault(dark: Bool = true) { 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) themeColor = dark ? Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
// 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() saveThemeColor()
} }
@@ -55,6 +56,7 @@ class ThemeSettings: ObservableObject {
let slate = colorScheme.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 slate = colorScheme.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 = colorScheme.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 solarized = colorScheme.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 = colorScheme.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) let dracula = colorScheme.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)
let macOS = colorScheme.colorScheme == .light ? Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) : Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1)
switch preset { switch preset {
case "slate": case "slate":
@@ -63,6 +65,8 @@ class ThemeSettings: ObservableObject {
return solarized return solarized
case "dracula": case "dracula":
return dracula return dracula
case "macOS":
return macOS
default: default:
return themeColor return themeColor
} }
+57 -6
View File
@@ -312,17 +312,14 @@ func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? {
// Get icon for files and folders // Get icon for files and folders
func getIconForFileOrFolder(atPath path: URL) -> Image? { func getIconForFileOrFolder(atPath path: URL) -> Image? {
let icon = NSWorkspace.shared.icon(forFile: path.path) return Image(nsImage: NSWorkspace.shared.icon(forFile: path.path))
let nsImage = icon
return Image(nsImage: nsImage)
} }
func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? { func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? {
let icon = NSWorkspace.shared.icon(forFile: path.path) return NSWorkspace.shared.icon(forFile: path.path)
let nsImage = icon
return nsImage
} }
// Get average color from image // Get average color from image
extension NSImage { extension NSImage {
var averageColor: NSColor? { var averageColor: NSColor? {
@@ -393,6 +390,60 @@ func removeApp(appState: AppState, withId id: UUID) {
} }
// Check if file/folder name has localized variant
func showLocalized(url: URL) -> String {
guard FileManager.default.fileExists(atPath: url.path) else {
return url.lastPathComponent
}
do {
// Retrieve the localized name
let resourceValues = try url.resourceValues(forKeys: [.localizedNameKey])
if let localizedName = resourceValues.localizedName {
return localizedName
}
} catch {
printOS("Error retrieving localized name: \(error)")
}
// Return the last path component as a fallback
return url.lastPathComponent
}
// Return image for different folders
func folderImages(for path: String) -> AnyView? {
if path.contains("/Library/Containers/") || path.contains("/Library/Group Containers/") {
return AnyView(
Image(systemName: "shippingbox.fill")
.resizable()
.scaledToFit()
.frame(width: 13)
.foregroundStyle(Color("mode").opacity(0.5))
.help("Container")
)
} else if path.contains("/Library/Application Scripts/") {
return AnyView(
Image(systemName: "applescript.fill")
.resizable()
.scaledToFit()
.frame(width: 13)
.foregroundStyle(Color("mode").opacity(0.5))
.help("Application Script")
)
} else if path.contains(".plist") {
return AnyView(
Image(systemName: "doc.badge.gearshape.fill")
.resizable()
.scaledToFit()
.frame(width: 13)
.foregroundStyle(Color("mode").opacity(0.5))
.help("Plist File")
)
}
// Return nil if no conditions are met
return nil
}
// Check if app bundle is nested // Check if app bundle is nested
func isNested(path: URL) -> Bool { func isNested(path: URL) -> Bool {
let applicationsPath = "/Applications" let applicationsPath = "/Applications"
+4 -2
View File
@@ -7,7 +7,7 @@
import SwiftUI import SwiftUI
import AppKit import AppKit
import ServiceManagement //import ServiceManagement
@main @main
struct PearcleanerApp: App { struct PearcleanerApp: App {
@@ -221,7 +221,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
UserDefaults.standard.set(displayMode?.rawValue, forKey: "displayMode") UserDefaults.standard.set(displayMode?.rawValue, forKey: "displayMode")
ThemeSettings.shared.themeColor = isDarkMode() ? 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) // Customize dark mode color to use Slate by default
ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
// ThemeSettings.shared.themeColor = isDarkMode() ? 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)
ThemeSettings.shared.saveThemeColor() ThemeSettings.shared.saveThemeColor()
} }
+38 -4
View File
@@ -7,7 +7,7 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
import ServiceManagement //import ServiceManagement
import FinderSync import FinderSync
struct GeneralSettingsTab: View { struct GeneralSettingsTab: View {
@@ -29,8 +29,8 @@ struct GeneralSettingsTab: View {
@State private var accessStatus: Bool = false @State private var accessStatus: Bool = false
@Binding var showPopover: Bool @Binding var showPopover: Bool
@Binding var search: String @Binding var search: String
@State var selectedIndex: Int? @State var selectedIndex: Int?
@State private var isResetting = false
var body: some View { var body: some View {
Form { Form {
@@ -290,6 +290,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(appState.finderExtensionEnabled ? .green : .red) .foregroundStyle(appState.finderExtensionEnabled ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -307,7 +308,20 @@ struct GeneralSettingsTab: View {
.padding(5) .padding(5)
.padding(.leading) .padding(.leading)
// === Reset Settings =============================================================================================
HStack() {
Spacer()
Button("") {
resetUserDefaults()
}
.buttonStyle(ResetSettingsButtonStyle(isResetting: $isResetting, label: "Reset Settings", help: "Reset all app settings to default"))
.disabled(isResetting)
Spacer()
}
.padding(.vertical, 5)
@@ -324,8 +338,28 @@ struct GeneralSettingsTab: View {
} }
.padding(20) .padding(20)
.frame(width: 500, height: 570) .frame(width: 500, height: 620)
} }
private var keysToIgnore: Set<String> {
["windowWidthKey", "windowHeightKey", "windowWidthKeyMini", "windowHeightKeyMini", "windowXKey", "windowYKey"]
}
private func resetUserDefaults() {
isResetting = true
DispatchQueue.global(qos: .background).async {
let defaults = UserDefaults.standard
let dictionary = defaults.dictionaryRepresentation()
dictionary.keys.forEach { key in
if !keysToIgnore.contains(key) {
defaults.removeObject(forKey: key)
}
}
DispatchQueue.main.async {
isResetting = false
}
}
}
} }
+70 -18
View File
@@ -21,10 +21,11 @@ struct InterfaceSettingsTab: View {
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
@AppStorage("settings.general.glass") private var glass: Bool = true @AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.general.dark") var isDark: Bool = true @AppStorage("settings.general.dark") var isDark: Bool = true
@AppStorage("settings.general.popover") private var popoverStay: Bool = true @AppStorage("settings.general.popover") private var popoverStay: Bool = true
@AppStorage("settings.general.miniview") private var miniView: Bool = true @AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto" @AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4" @AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4"
@State private var isLaunchAtLoginEnabled: Bool = false @State private var isLaunchAtLoginEnabled: Bool = false
@@ -32,6 +33,14 @@ struct InterfaceSettingsTab: View {
@Binding var search: String @Binding var search: String
let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"] let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"]
var isMacOS14OrHigher: Bool {
if #available(macOS 14, *) {
return true
} else {
return false
}
}
var body: some View { var body: some View {
Form { Form {
@@ -67,6 +76,38 @@ struct InterfaceSettingsTab: View {
.padding(.leading) .padding(.leading)
HStack(spacing: 0) {
Image(systemName: animateLogo ? "play.fill" : "pause")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(animateLogo ? "Logo animation enabled" : "Logo animation disabled")")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
}
if !isMacOS14OrHigher {
Text("(macOS 14+)")
.font(.footnote)
.foregroundStyle(Color("mode").opacity(0.3))
.padding(.leading, 5)
}
// InfoButton(text: "The logo animation is only available in macOS 14 or higher", color: nil, label: "")
Spacer()
Toggle(isOn: $animateLogo, label: {
})
.toggleStyle(.switch)
.disabled(!isMacOS14OrHigher)
}
.padding(5)
.padding(.leading)
HStack(spacing: 0) { HStack(spacing: 0) {
Image(systemName: "paintbrush") Image(systemName: "paintbrush")
.resizable() .resizable()
@@ -82,22 +123,33 @@ struct InterfaceSettingsTab: View {
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: "") 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() Spacer()
Button("") {
themeSettings.setPreset(preset: "slate", colorScheme: displayMode)
}
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "slate", colorScheme: displayMode), label: "Slate"))
Button("") { HStack(spacing: 10) {
themeSettings.setPreset(preset: "dracula", colorScheme: displayMode) Button("") {
} themeSettings.setPreset(preset: "slate", colorScheme: displayMode)
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "dracula", colorScheme: displayMode), label: "Dracula")) }
.padding(.horizontal) .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "slate", colorScheme: displayMode), label: "Slate"))
Button("") { Button("") {
themeSettings.setPreset(preset: "solarized", colorScheme: displayMode) themeSettings.setPreset(preset: "dracula", colorScheme: displayMode)
themeSettings.saveThemeColor() }
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "dracula", colorScheme: displayMode), label: "Dracula"))
Button("") {
themeSettings.setPreset(preset: "solarized", colorScheme: displayMode)
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "solarized", colorScheme: displayMode), label: "Solarized"))
Button("") {
themeSettings.setPreset(preset: "macOS", colorScheme: displayMode)
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "macOS", colorScheme: displayMode), label: "macOS"))
} }
.buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "solarized", colorScheme: displayMode), label: "Solarized"))
Spacer() Spacer()
@@ -106,6 +158,7 @@ struct InterfaceSettingsTab: View {
themeSettings.saveThemeColor() themeSettings.saveThemeColor()
} }
.padding(.horizontal, 5) .padding(.horizontal, 5)
Button("") { Button("") {
themeSettings.resetToDefault(dark: colorScheme == .dark) themeSettings.resetToDefault(dark: colorScheme == .dark)
} }
@@ -182,7 +235,6 @@ struct InterfaceSettingsTab: View {
} else { } else {
displayMode.colorScheme = .light displayMode.colorScheme = .light
} }
// displayMode.colorScheme = nil
themeSettings.resetToDefault(dark: isDarkMode()) themeSettings.resetToDefault(dark: isDarkMode())
// Refresh foreground colors // Refresh foreground colors
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
@@ -193,7 +245,7 @@ struct InterfaceSettingsTab: View {
} }
case "Dark": case "Dark":
displayMode.colorScheme = .dark displayMode.colorScheme = .dark
// themeSettings.resetToDefault(dark: true) themeSettings.resetToDefault(dark: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface self.selectedTab = .interface
} }
@@ -202,7 +254,7 @@ struct InterfaceSettingsTab: View {
} }
case "Light": case "Light":
displayMode.colorScheme = .light displayMode.colorScheme = .light
// themeSettings.resetToDefault(dark: false) themeSettings.resetToDefault(dark: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface self.selectedTab = .interface
} }
@@ -506,7 +558,7 @@ struct InterfaceSettingsTab: View {
} }
.padding(20) .padding(20)
.frame(width: 500, height: 570) .frame(width: 500, height: 600)
} }
+1 -1
View File
@@ -14,7 +14,7 @@ struct SettingsView: View {
@Binding var showPopover: Bool @Binding var showPopover: Bool
@Binding var search: String @Binding var search: String
@Binding var showFeature: Bool @Binding var showFeature: Bool
@AppStorage("settings.general.glass") private var glass: Bool = true @AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
var body: some View { var body: some View {
+55 -75
View File
@@ -18,47 +18,44 @@ struct AppListItems: View {
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.miniview") private var miniView: Bool = true @AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.glass") private var glass: Bool = true
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
@Binding var showPopover: Bool @Binding var showPopover: Bool
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
let itemId = UUID() let itemId = UUID()
let appInfo: AppInfo let appInfo: AppInfo
var isSelected: Bool { var isSelected: Bool {
appState.appInfo.path == appInfo.path appState.appInfo.id == appInfo.id
} }
@State private var hoveredItemPath: URL? = nil @State private var hoveredItemPath: URL? = nil
var body: some View { var body: some View {
HStack(alignment: .center) { ZStack() {
if (isHovered || isSelected) && mini { HStack(alignment: .center) {
RoundedRectangle(cornerRadius: 50)
.fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 4 : 2, height: 25)
.padding(.trailing, 5)
}
if let appIcon = appInfo.appIcon { if let appIcon = appInfo.appIcon {
ZStack { ZStack {
Image(nsImage: appIcon) Image(nsImage: appIcon)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30) .frame(width: (isHovered || isSelected) ? 32 : 30, height: (isHovered || isSelected) ? 32 : 30)
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(color: .black.opacity(0.5), radius: 2, y: 2) // .shadow(color: Color("pop").opacity(0.8), radius: isSelected ? 2 : 0)
} }
} }
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text(appInfo.appName) Text(appInfo.appName)
.font(.system(size: 12)) .font(.system(size: (isHovered || isSelected) ? 14 : 12))
.lineLimit(1) .lineLimit(1)
.truncationMode(.tail) .truncationMode(.tail)
.padding(.horizontal, 5) .padding(.horizontal, 5)
} }
Spacer() Spacer()
if appInfo.webApp { if appInfo.webApp {
@@ -83,75 +80,58 @@ struct AppListItems: View {
} }
Text(appInfo.appVersion) Text(appInfo.appVersion)
.font(.footnote) .font(.system(size: (isHovered || isSelected) ? 12 : 10))
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
if (isHovered || isSelected) && !mini { // if (isHovered || isSelected) {
Triangle() // RoundedRectangle(cornerRadius: 50)
.fill(themeSettings.themeColor) // .fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 16 : 8, height: 30) // .frame(width: isSelected ? 4 : 2, height: 25)
.padding(.leading, 5) // .padding(.trailing, 5)
.offset(x: 22) // }
.zIndex(5)
}
}
.frame(height: 35)
.padding(.horizontal)
.padding(.vertical, 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)) {
if isSelected {
appState.appInfo = .empty
appState.selectedItems = []
appState.currentView = miniView ? .apps : .empty
showPopover = false
} else {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
}
}
}
.background{
Rectangle()
.fill(isSelected && !glass ? themeSettings.themeColor : .clear)
}
.overlay{
if (isHovered || isSelected) {
HStack {
Spacer()
RoundedRectangle(cornerRadius: 50)
.fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 4 : 2, height: 25)
.padding(.trailing, 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)) {
if isSelected {
appState.appInfo = .empty
appState.currentView = miniView ? .apps : .empty
showPopover = false
} else {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
}
}
}
}
func opacityForItem(_ path: URL) -> Double {
// Check if any item is selected
let isAnyItemSelected = appState.sortedApps.contains(where: { $0.path == appState.appInfo.path })
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
}
}
+3 -3
View File
@@ -30,7 +30,7 @@ struct AppsListView: View {
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover) SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
} }
} }
.padding(.horizontal) // .padding(.horizontal)
.padding(.top, !mini ? 4 : 0) .padding(.top, !mini ? 4 : 0)
} }
.scrollIndicators(.never) .scrollIndicators(.never)
@@ -47,11 +47,11 @@ struct SectionView: View {
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
Header(title: title, count: count, showPopover: $showPopover) Header(title: title, count: count, showPopover: $showPopover)
.padding(.leading, 5)
ForEach(apps, id: \.self) { appInfo in ForEach(apps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo) AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical, 5)
if appInfo != apps.last { if appInfo != apps.last {
Divider().padding(.horizontal, 5) // Divider().padding(.horizontal, 5)
} }
} }
} }
+13 -6
View File
@@ -110,8 +110,6 @@ struct FilesView: View {
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
// .clipShape(RoundedRectangle(cornerRadius: 8))
// .padding(.trailing)
.padding() .padding()
.background{ .background{
RoundedRectangle(cornerRadius: 16) RoundedRectangle(cornerRadius: 16)
@@ -288,15 +286,17 @@ struct FilesView: View {
showPopover = false showPopover = false
updateOnMain { updateOnMain {
appState.currentView = mini ? .apps : .empty appState.currentView = mini ? .apps : .empty
appState.isReminderVisible.toggle() // appState.isReminderVisible.toggle()
} }
if sentinel { if sentinel {
launchctl(load: true) launchctl(load: true)
} }
} }
// Remove app from app list if main app bundle is removed // Remove app from app list if main app bundle is removed for regular and wrapped apps
if selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString }) { if (appState.appInfo.wrapped && selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.deletingLastPathComponent().deletingLastPathComponent().absoluteString })) ||
(!appState.appInfo.wrapped && selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString })) {
// Match found, remove the app
removeApp(appState: appState, withId: appState.appInfo.id) removeApp(appState: appState, withId: appState.appInfo.id)
} else { } else {
// Add deleted appInfo object to trashed array // Add deleted appInfo object to trashed array
@@ -373,16 +373,23 @@ struct FileDetailsItem: View {
.shadow(color: Color("mode"), radius: isHovered ? 2 : 0) .shadow(color: Color("mode"), radius: isHovered ? 2 : 0)
} }
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .center) { HStack(alignment: .center) {
Text(path.lastPathComponent) Text(showLocalized(url: path))
.font(.title3) .font(.title3)
.lineLimit(1) .lineLimit(1)
.truncationMode(.tail) .truncationMode(.tail)
.help(path.lastPathComponent) .help(path.lastPathComponent)
if isNested(path: path) { if isNested(path: path) {
InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.", color: nil, label: "") InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.", color: nil, label: "")
} }
if let imageView = folderImages(for: path.path) {
imageView
}
} }
Text(path.path) Text(path.path)
+2 -46
View File
@@ -74,7 +74,6 @@ struct MenuBarMiniAppView: View {
.padding(.leading, 10) .padding(.leading, 10)
SearchBarMiniBottom(search: $search) SearchBarMiniBottom(search: $search)
// .padding(.leading, 20)
Button("More") { Button("More") {
self.showMenu.toggle() self.showMenu.toggle()
@@ -105,26 +104,11 @@ struct MenuBarMiniAppView: View {
.padding() .padding()
.background(backgroundView(themeSettings: themeSettings, glass: glass).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)
//
// }
// }
// )
} }
} }
// .padding(.horizontal) .padding(.top, 5)
} }
.padding(.vertical, 8) .padding(.vertical, 8)
@@ -137,20 +121,6 @@ struct MenuBarMiniAppView: View {
.frame(minWidth: 300, minHeight: 370) .frame(minWidth: 300, minHeight: 370)
.edgesIgnoringSafeArea(.all) .edgesIgnoringSafeArea(.all)
.background(backgroundView(themeSettings: themeSettings, glass: glass).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) .transition(.opacity)
.popover(isPresented: $showPopover, arrowEdge: .leading) { .popover(isPresented: $showPopover, arrowEdge: .leading) {
VStack { VStack {
@@ -165,20 +135,6 @@ struct MenuBarMiniAppView: View {
} }
.interactiveDismissDisabled(popoverStay) .interactiveDismissDisabled(popoverStay)
.background(backgroundView(themeSettings: themeSettings, glass: glass).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) .frame(width: 650, height: 500)
} }
+64 -28
View File
@@ -93,6 +93,7 @@ struct MiniEmptyView: View {
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@State private var animateGradient: Bool = false @State private var animateGradient: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
@Binding var showPopover: Bool @Binding var showPopover: Bool
var body: some View { var body: some View {
@@ -100,18 +101,52 @@ struct MiniEmptyView: View {
Spacer() Spacer()
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing) if #available(macOS 14, *) {
.mask( if animateLogo {
Image(systemName: "plus.square.dashed") LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.resizable() .mask(
.scaledToFit() Image(systemName: "plus.square.dashed")
.frame(width: 120, height: 120, alignment: .center) .resizable()
.padding() .scaledToFit()
.fontWeight(.ultraLight) .frame(width: 120, height: 120, alignment: .center)
.offset(x: 5, y: 5) .padding()
) .fontWeight(.ultraLight)
.offset(x: 5, y: 5)
)
.phaseAnimator([false, true]) { wwdc24, chromaRotate in
wwdc24
.hueRotation(.degrees(chromaRotate ? 420 : 0))
} animation: { chromaRotate in
.easeInOut(duration: 6)
}
} else {
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(systemName: "plus.square.dashed")
.resizable()
.scaledToFit()
.frame(width: 120, height: 120, alignment: .center)
.padding()
.fontWeight(.ultraLight)
.offset(x: 5, y: 5)
)
}
} else {
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(systemName: "plus.square.dashed")
.resizable()
.scaledToFit()
.frame(width: 120, height: 120, alignment: .center)
.padding()
.fontWeight(.ultraLight)
.offset(x: 5, y: 5)
)
}
Text("Drop your app here")
Text("Drop an app here")
.font(.title3) .font(.title3)
.padding(.bottom, 25) .padding(.bottom, 25)
.opacity(0.5) .opacity(0.5)
@@ -120,23 +155,23 @@ struct MiniEmptyView: View {
Spacer() Spacer()
if appState.isReminderVisible { // if appState.isReminderVisible {
Text("CMD + Z to undo") // Text("CMD + Z to undo")
.font(.title2) // .font(.title2)
.foregroundStyle(Color("mode").opacity(0.5)) // .foregroundStyle(Color("mode").opacity(0.5))
.fontWeight(.medium) // .fontWeight(.medium)
.onAppear { // .onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation { // withAnimation {
updateOnMain { // updateOnMain {
appState.isReminderVisible = false // appState.isReminderVisible = false
} // }
} // }
} // }
} // }
} // }
//
Spacer() // Spacer()
} }
@@ -186,6 +221,7 @@ struct MiniAppView: View {
if appState.currentView != .empty { if appState.currentView != .empty {
SearchBarMiniBottom(search: $search) SearchBarMiniBottom(search: $search)
.padding(.horizontal) .padding(.horizontal)
.padding(.top, 5)
} }
} }
.padding(.bottom) .padding(.bottom)
+19 -4
View File
@@ -59,7 +59,7 @@ struct RegularMode: View {
} }
.frame(width: sidebarWidth) .frame(width: sidebarWidth)
.padding(.vertical) .padding(.vertical, 7)
} }
@@ -107,19 +107,34 @@ struct AppDetailsEmptyView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@State private var animateGradient: Bool = false @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
@Binding var showPopover: Bool @Binding var showPopover: Bool
var body: some View { var body: some View {
VStack(alignment: .center) { VStack(alignment: .center) {
Spacer() Spacer()
if #available(macOS 14, *) {
if animateLogo {
PearDropView()
.phaseAnimator([false, true]) { wwdc24, chromaRotate in
wwdc24
.hueRotation(.degrees(chromaRotate ? 420 : 0))
} animation: { chromaRotate in
.easeInOut(duration: 6)
}
} else {
PearDropView()
}
} else {
PearDropView()
}
PearDropView()
Spacer() Spacer()
Text("Drop your app here or select one from the list") Text("Drop an app here")
.font(.title3) .font(.title3)
.padding(.bottom, 25) .padding(.bottom, 25)
.opacity(0.5) .opacity(0.5)
+15 -15
View File
@@ -20,21 +20,21 @@ struct TopBar: View {
var body: some View { var body: some View {
HStack(alignment: .center, spacing: 0) { HStack(alignment: .center, spacing: 0) {
Spacer() // Spacer()
//
if appState.isReminderVisible { // if appState.isReminderVisible {
Text("CMD + Z to undo") // Text("CMD + Z to undo")
.font(.title2) // .font(.title2)
.foregroundStyle(Color("mode").opacity(0.5)) // .foregroundStyle(Color("mode").opacity(0.5))
.fontWeight(.medium) // .fontWeight(.medium)
.onAppear { // .onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation { // withAnimation {
appState.isReminderVisible = false // appState.isReminderVisible = false
} // }
} // }
} // }
} // }
Spacer() Spacer()
+16 -8
View File
@@ -255,9 +255,9 @@ struct ZombieView: View {
moveFilesToTrash(at: selectedItemsArray) { moveFilesToTrash(at: selectedItemsArray) {
withAnimation { withAnimation {
showPopover = false showPopover = false
updateOnMain { // updateOnMain {
appState.isReminderVisible.toggle() // appState.isReminderVisible.toggle()
} // }
} }
} }
} }
@@ -321,11 +321,19 @@ struct ZombieFileDetailsItem: View {
} }
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text(path.lastPathComponent)
.font(.title3) HStack(alignment: .center) {
.lineLimit(1) Text(showLocalized(url: path))
.truncationMode(.tail) .font(.title3)
.help(path.lastPathComponent) .lineLimit(1)
.truncationMode(.tail)
.help(path.lastPathComponent)
if let imageView = folderImages(for: path.path) {
imageView
}
}
Text(path.path) Text(path.path)
.font(.footnote) .font(.footnote)
.lineLimit(1) .lineLimit(1)