This commit is contained in:
Alin
2024-03-01 18:28:21 -07:00
parent e87bbe0c9f
commit c3ce997a88
37 changed files with 783 additions and 2211 deletions
+8 -20
View File
@@ -19,7 +19,6 @@
C7575D392B0182DE006A600A /* PearcleanerSentinel in CopyFiles */ = {isa = PBXBuildFile; fileRef = C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; C7575D392B0182DE006A600A /* PearcleanerSentinel in CopyFiles */ = {isa = PBXBuildFile; fileRef = C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
C76D08482AF83C3F00D07867 /* Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08472AF83C3F00D07867 /* Update.swift */; }; C76D08482AF83C3F00D07867 /* Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08472AF83C3F00D07867 /* Update.swift */; };
C76D084A2AF83C6E00D07867 /* General.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08492AF83C6E00D07867 /* General.swift */; }; C76D084A2AF83C6E00D07867 /* General.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08492AF83C6E00D07867 /* General.swift */; };
C76D084C2AF83C9D00D07867 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D084B2AF83C9D00D07867 /* Permissions.swift */; };
C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; }; C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; };
C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* AppListView.swift */; }; C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* AppListView.swift */; };
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; }; C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; };
@@ -32,13 +31,11 @@
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90222AF2D616009CC655 /* FilesView.swift */; }; C77B90232AF2D616009CC655 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90222AF2D616009CC655 /* FilesView.swift */; };
C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */ = {isa = PBXBuildFile; fileRef = C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */; }; C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */ = {isa = PBXBuildFile; fileRef = C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */; };
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A9CE462B89164700EB6E78 /* Authorization.swift */; }; C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A9CE462B89164700EB6E78 /* Authorization.swift */; };
C7ACADE62B92A737000B5845 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ACADE52B92A737000B5845 /* Features.swift */; };
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CF47232B3B3F1700979C5F /* WindowSettings.swift */; }; C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CF47232B3B3F1700979C5F /* WindowSettings.swift */; };
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D452AFDA90C00C7ED9E /* Sentinel.swift */; };
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D472AFEB23700C7ED9E /* TopBar.swift */; }; C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D472AFEB23700C7ED9E /* TopBar.swift */; };
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; }; C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; }; C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D542B008CB900C7ED9E /* Shapes.swift */; };
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D582B00A49300C7ED9E /* DropTarget.swift */; };
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; }; C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -76,7 +73,6 @@
C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; }; C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
C76D08472AF83C3F00D07867 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = "<group>"; }; C76D08472AF83C3F00D07867 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = "<group>"; };
C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = "<group>"; }; C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = "<group>"; };
C76D084B2AF83C9D00D07867 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; }; C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
C76D08542AF89CDE00D07867 /* AppListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListView.swift; sourceTree = "<group>"; }; C76D08542AF89CDE00D07867 /* AppListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListView.swift; sourceTree = "<group>"; };
C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; }; C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -93,13 +89,11 @@
C7A27E7F2AFD70DD00166168 /* PearcleanerSentinel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PearcleanerSentinel.entitlements; 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>"; }; 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>"; }; C7A9CE462B89164700EB6E78 /* Authorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = "<group>"; };
C7ACADE52B92A737000B5845 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSettings.swift; sourceTree = "<group>"; }; C7CF47232B3B3F1700979C5F /* WindowSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSettings.swift; sourceTree = "<group>"; };
C7D31D452AFDA90C00C7ED9E /* Sentinel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sentinel.swift; sourceTree = "<group>"; };
C7D31D472AFEB23700C7ED9E /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = "<group>"; }; C7D31D472AFEB23700C7ED9E /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = "<group>"; };
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; }; C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; }; C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
C7D31D542B008CB900C7ED9E /* Shapes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shapes.swift; sourceTree = "<group>"; };
C7D31D582B00A49300C7ED9E /* DropTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropTarget.swift; sourceTree = "<group>"; };
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; }; C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -142,9 +136,9 @@
C72893042AFD42E600C8C1CD /* DeepLink.swift */, C72893042AFD42E600C8C1CD /* DeepLink.swift */,
C77B901B2AF193A3009CC655 /* Styles.swift */, C77B901B2AF193A3009CC655 /* Styles.swift */,
C7D31D502AFF00F300C7ED9E /* Locations.swift */, C7D31D502AFF00F300C7ED9E /* Locations.swift */,
C7D31D542B008CB900C7ED9E /* Shapes.swift */,
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */, C7CF47232B3B3F1700979C5F /* WindowSettings.swift */,
C7A9CE462B89164700EB6E78 /* Authorization.swift */, C7A9CE462B89164700EB6E78 /* Authorization.swift */,
C7ACADE52B92A737000B5845 /* Features.swift */,
); );
path = Logic; path = Logic;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -171,8 +165,6 @@
children = ( children = (
C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */, C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */,
C76D08492AF83C6E00D07867 /* General.swift */, C76D08492AF83C6E00D07867 /* General.swift */,
C76D084B2AF83C9D00D07867 /* Permissions.swift */,
C7D31D452AFDA90C00C7ED9E /* Sentinel.swift */,
C76D08472AF83C3F00D07867 /* Update.swift */, C76D08472AF83C3F00D07867 /* Update.swift */,
C76D08502AF850F300D07867 /* About.swift */, C76D08502AF850F300D07867 /* About.swift */,
); );
@@ -230,7 +222,6 @@
C7D31D472AFEB23700C7ED9E /* TopBar.swift */, C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
C7045A292B03FAF000376976 /* TopBarMini.swift */, C7045A292B03FAF000376976 /* TopBarMini.swift */,
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */, C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
C7D31D582B00A49300C7ED9E /* DropTarget.swift */,
C7045A272B03E71D00376976 /* MiniMode.swift */, C7045A272B03E71D00376976 /* MiniMode.swift */,
C71848432B8D2D600046CB13 /* ZombieView.swift */, C71848432B8D2D600046CB13 /* ZombieView.swift */,
); );
@@ -358,23 +349,20 @@
C7045A322B068AD700376976 /* NewWin.swift in Sources */, C7045A322B068AD700376976 /* NewWin.swift in Sources */,
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */, C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */, C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */,
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */,
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */, C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */, C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */, C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */, C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */,
C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */, C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */,
C76D084C2AF83C9D00D07867 /* Permissions.swift in Sources */,
C76D084A2AF83C6E00D07867 /* General.swift in Sources */, C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */, C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */,
C7045A362B068DD700376976 /* PermView.swift in Sources */, C7045A362B068DD700376976 /* PermView.swift in Sources */,
C76D08512AF850F300D07867 /* About.swift in Sources */, C76D08512AF850F300D07867 /* About.swift in Sources */,
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */, C77B90182AF19382009CC655 /* AppCommands.swift in Sources */,
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */, C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */,
C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */, C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */,
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */, C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */,
C7ACADE62B92A737000B5845 /* Features.swift in Sources */,
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */, C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */,
C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */, C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */,
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */, C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
@@ -551,7 +539,7 @@
"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 = 18; CURRENT_PROJECT_VERSION = 19;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
@@ -569,7 +557,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7; MARKETING_VERSION = 3.0.0;
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;
@@ -586,7 +574,7 @@
"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 = 18; CURRENT_PROJECT_VERSION = 19;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU; DEVELOPMENT_TEAM = BK8443AXLU;
@@ -604,7 +592,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7; MARKETING_VERSION = 3.0.0;
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;
+2 -6
View File
@@ -46,12 +46,6 @@ struct AppCommands: Commands {
updateOnMain { updateOnMain {
appState.reload.toggle() appState.reload.toggle()
} }
// let sortedApps = getSortedApps()
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// appState.sortedApps.userApps = sortedApps.userApps
// appState.sortedApps.systemApps = sortedApps.systemApps
// appState.reload.toggle()
// }
} }
} label: { } label: {
Text("Refresh Apps") Text("Refresh Apps")
@@ -86,6 +80,8 @@ struct AppCommands: Commands {
Label("Undo Removal", systemImage: "clear") Label("Undo Removal", systemImage: "clear")
} }
.keyboardShortcut("z", modifiers: .command) .keyboardShortcut("z", modifiers: .command)
} }
-76
View File
@@ -48,7 +48,6 @@ class AppState: ObservableObject
self.zombieFile = ZombieFile( self.zombieFile = ZombieFile(
id: UUID(), id: UUID(),
// files: [],
fileSize: [:], fileSize: [:],
fileIcon: [:] fileIcon: [:]
) )
@@ -86,7 +85,6 @@ struct AppInfo: Identifiable, Equatable, Hashable {
struct ZombieFile: Identifiable, Equatable, Hashable { struct ZombieFile: Identifiable, Equatable, Hashable {
let id: UUID let id: UUID
// var files: [URL]
var fileSize: [URL:Int64] var fileSize: [URL:Int64]
var fileIcon: [URL:NSImage?] var fileIcon: [URL:NSImage?]
var totalSize: Int64 var totalSize: Int64
@@ -184,78 +182,4 @@ enum DisplayMode: Int, CaseIterable {
} }
//class ProgressManager: ObservableObject {
// @Published var progress: Double = 0.0
// @Published var total: Double = 0.0
// @Published var status: String = "Ready"
//
// func setTotal(_ total: Double) {
// DispatchQueue.main.async {
// self.total = total
// }
// }
//
// func updateProgress() {
// DispatchQueue.main.async {
// self.progress = min(max(0.0, self.progress + 1.0), Double(self.total))
// }
// }
//
// func updateStatus(status: String) {
// DispatchQueue.main.async {
// self.status = status
// }
// }
//
// func resetProgress() {
// DispatchQueue.main.async {
// self.progress = 0.0
// }
// }
//}
//let (cacheDir, tempDir) = darwinCT()
//let locations: [String] = [
// "\(home)/Library",
// "\(home)/Library/Application Scripts",
// "\(home)/Library/Application Support",
// "\(home)/Library/Application Support/CrashReporter",
// "\(home)/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments",
// "\(home)/Library/Containers",
// "\(home)/Library/Group Containers",
// "\(home)/Library/Caches",
// "\(home)/Library/HTTPStorages",
// "\(home)/Library/Group Containers",
// "\(home)/Library/Internet Plug-Ins",
// "\(home)/Library/LaunchAgents",
// "\(home)/Library/Logs",
// "\(home)/Library/Preferences",
// "\(home)/Library/Preferences/ByHost",
// "\(home)/Library/Saved Application State",
// "\(home)/Library/WebKit",
// "/Users/Shared",
// "/Users/Library",
// "/Library",
// "/Library/Application Support",
// "/Library/Application Support/CrashReporter",
// "/Library/Caches",
// "/Library/Extensions",
// "/Library/Internet Plug-Ins",
// "/Library/LaunchAgents",
// "/Library/LaunchDaemons",
// "/Library/Logs",
// "/Library/Logs/DiagnosticReports",
// "/Library/Preferences",
// "/Library/PrivilegedHelperTools",
// "/private/var/db/receipts",
// "/private/tmp",
// "/usr/local/bin",
// "/usr/local/etc",
// "/usr/local/opt",
// "/usr/local/sbin",
// "/usr/local/share",
// "/usr/local/var",
// cacheDir,
// tempDir
//]
-69
View File
@@ -35,36 +35,6 @@ class DeeplinkManager {
let pathURL = URL(fileURLWithPath: path) let pathURL = URL(fileURLWithPath: path)
let appInfo = getAppInfo(atPath: pathURL) let appInfo = getAppInfo(atPath: pathURL)
showAppInFiles(appInfo: appInfo!, mini: mini, appState: appState, locations: locations, showPopover: $showPopover) showAppInFiles(appInfo: appInfo!, mini: mini, appState: appState, locations: locations, showPopover: $showPopover)
// showPopover = false
// updateOnMain {
// appState.appInfo = .empty
// if let storedAppInfo = appState.appInfoStore.first(where: { $0.path == appInfo?.path }) {
// appState.appInfo = storedAppInfo
//// appState.paths = storedAppInfo.fileSize.keys.map { $0 }//storedAppInfo.files
// appState.selectedItems = Set(storedAppInfo.files)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if self.mini {
// self.showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// } else {
// // Handle the case where the appInfo is not found in the store
// printOS("AppInfo not found in the cached store, searching again")
// appState.appInfo = .empty
// appState.appInfo = appInfo!
// findPathsForApp(appInfo: appInfo!, appState: appState, locations: locations)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if self.mini {
// self.showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// }
// }
} else { } else {
printOS("No path query parameter found in the URL") printOS("No path query parameter found in the URL")
} }
@@ -77,45 +47,6 @@ class DeeplinkManager {
func handleAppBundle(url: URL, appState: AppState, locations: Locations) { func handleAppBundle(url: URL, appState: AppState, locations: Locations) {
let appInfo = getAppInfo(atPath: url) let appInfo = getAppInfo(atPath: url)
showAppInFiles(appInfo: appInfo!, mini: mini, appState: appState, locations: locations, showPopover: $showPopover) showAppInFiles(appInfo: appInfo!, mini: mini, appState: appState, locations: locations, showPopover: $showPopover)
// showPopover = false
// updateOnMain {
// appState.appInfo = .empty
// if let storedAppInfo = appState.appInfoStore.first(where: { $0.path == appInfo?.path }) {
// appState.appInfo = storedAppInfo
//// appState.paths = storedAppInfo.fileSize.keys.map { $0 }//storedAppInfo.files
// appState.selectedItems = Set(storedAppInfo.files)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if self.mini {
// self.showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// } else {
// // Handle the case where the appInfo is not found in the store
// printOS("AppInfo not found in the cached store, searching again")
// appState.appInfo = .empty
// appState.appInfo = appInfo!
// findPathsForApp(appInfo: appInfo!, appState: appState, locations: locations)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if self.mini {
// self.showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// }
// }
// updateOnMain {
// appState.appInfo = appInfo!
// findPathsForApp(appState: appState, locations: locations)
// if self.mini {
// self.showPopover = true
// } else {
// appState.currentView = .files
// }
// }
} }
} }
+49
View File
@@ -0,0 +1,49 @@
//
// Features.swift
// Pearcleaner
//
// Created by Alin Lupascu on 3/1/24.
//
import Foundation
import SwiftUI
func getFeatures(appState: AppState, show: Binding<Bool>, features: Binding<String>) {
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/features.json")!
var request = URLRequest(url: url)
request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept")
request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version")
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
if let jsonDict = jsonObject as? [String: String],
let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
let featureText = jsonDict[bundleVersion] {
if features.wrappedValue != featureText.featureFormat() {
updateOnMain {
features.wrappedValue = featureText.featureFormat()
show.wrappedValue = true
}
} else {
show.wrappedValue = false
// print("Same version")
}
} else {
show.wrappedValue = false
// print("No features for version found")
}
} catch {
show.wrappedValue = false
printOS("Error reading features JSON from GitHub: \(error.localizedDescription)")
}
} else {
show.wrappedValue = false
printOS("Error reading features JSON from GitHub: \(error?.localizedDescription ?? "Unknown error")")
}
}.resume()
}
+58 -185
View File
@@ -7,8 +7,6 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
//import AudioToolbox
// Get list of apps and sort it // Get list of apps and sort it
func getSortedApps() -> (systemApps: [AppInfo], userApps: [AppInfo]) { func getSortedApps() -> (systemApps: [AppInfo], userApps: [AppInfo]) {
@@ -417,7 +415,7 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
// Append group containers // Append group containers
let groupContainers = getGroupContainers(bundleURL: appState.appInfo.path) let groupContainers = getGroupContainers(bundleURL: appState.appInfo.path)
collection.append(contentsOf: groupContainers) collection.append(contentsOf: groupContainers)
let sortedCollection = collection.sorted(by: { $0.absoluteString < $1.absoluteString }) var sortedCollection = collection.sorted(by: { $0.absoluteString < $1.absoluteString })
// Calculate file details (sizes and icons) // Calculate file details (sizes and icons)
@@ -435,6 +433,11 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
fileIcon[path] = icon fileIcon[path] = icon
} }
if sortedCollection.count == 1 {
if let firstURL = sortedCollection.first, firstURL.path.contains(".Trash") {
sortedCollection = []
}
}
// Save to appState // Save to appState
dispatchGroup.notify(queue: .main) { dispatchGroup.notify(queue: .main) {
@@ -457,39 +460,61 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
// Load app paths on launch // Load app paths on launch
func loadAllPaths(allApps: [AppInfo], appState: AppState, locations: Locations) { func loadAllPaths(allApps: [AppInfo], appState: AppState, locations: Locations, reverseAddon: Bool = false, completion: @escaping () -> Void = {}) {
let dispatchGroup = DispatchGroup()
let retryLimit = 120
var currentRetryCount = 0
func checkCompletion() {
if appState.appInfoStore.count == allApps.count {
if reverseAddon {
reversePathsSearch(appState: appState, locations: locations)
}
completion()
} else {
if currentRetryCount < retryLimit {
currentRetryCount += 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
checkCompletion()
}
} else {
printOS("loadAllPaths - Retry limit timed out. Unable to load all paths within 1 minute.")
}
}
}
dispatchGroup.enter()
updateOnMain { updateOnMain {
appState.appInfoStore.removeAll() appState.appInfoStore.removeAll()
} }
dispatchGroup.notify(queue: .main) {
checkCompletion()
}
DispatchQueue.global(qos: .background).async {
for app in allApps { for app in allApps {
findPathsForApp(appInfo: app, appState: appState, locations: locations, backgroundRun: true) { findPathsForApp(appInfo: app, appState: appState, locations: locations, backgroundRun: true)
} }
dispatchGroup.leave()
} }
} }
// Load item in Files view // Load item in Files view
func showAppInFiles(appInfo: AppInfo, mini: Bool, appState: AppState, locations: Locations, showPopover: Binding<Bool>) { func showAppInFiles(appInfo: AppInfo, mini: Bool, appState: AppState, locations: Locations, showPopover: Binding<Bool>) {
showPopover.wrappedValue = false showPopover.wrappedValue = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
updateOnMain { updateOnMain {
appState.appInfo = .empty appState.appInfo = .empty
appState.selectedItems = []
if let storedAppInfo = appState.appInfoStore.first(where: { $0.path == appInfo.path }) { if let storedAppInfo = appState.appInfoStore.first(where: { $0.path == appInfo.path }) {
appState.appInfo = storedAppInfo appState.appInfo = storedAppInfo
appState.selectedItems = Set(storedAppInfo.files) appState.selectedItems = Set(storedAppInfo.files)
withAnimation(Animation.easeIn(duration: 0.3)) {
if mini {
appState.currentView = .files
showPopover.wrappedValue.toggle()
} else {
appState.currentView = .files
}
}
} else {
updateOnMain {
appState.showProgress = true
}
// Handle the case where the appInfo is not found in the store
withAnimation(Animation.easeIn(duration: 0.4)) { withAnimation(Animation.easeIn(duration: 0.4)) {
if mini { if mini {
appState.currentView = .files appState.currentView = .files
@@ -498,17 +523,25 @@ func showAppInFiles(appInfo: AppInfo, mini: Bool, appState: AppState, locations:
appState.currentView = .files appState.currentView = .files
} }
} }
printOS("AppInfo not found in the cached store, searching again") } else {
appState.appInfo = .empty // Handle the case where the appInfo is not found in the store
withAnimation(Animation.easeIn(duration: 0.4)) {
appState.showProgress = true
if mini {
appState.currentView = .files
showPopover.wrappedValue.toggle()
} else {
appState.currentView = .files
}
}
appState.appInfo = appInfo appState.appInfo = appInfo
findPathsForApp(appInfo: appInfo, appState: appState, locations: locations) { findPathsForApp(appInfo: appInfo, appState: appState, locations: locations) {
withAnimation(Animation.easeIn(duration: 0.4)) {
updateOnMain { updateOnMain {
appState.selectedItems = Set(appInfo.files)
appState.showProgress = false appState.showProgress = false
} }
} }
}
} }
} }
@@ -562,6 +595,7 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
let allNames = appState.appInfoStore.map { $0.appName.pearFormat() } let allNames = appState.appInfoStore.map { $0.appName.pearFormat() }
let skipped = ["apple", "comapple", "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", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandobxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter"] // Skip system folders let skipped = ["apple", "comapple", "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", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandobxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter"] // Skip system folders
// Skip locations that might not exist // Skip locations that might not exist
for location in locations.reverse.paths { for location in locations.reverse.paths {
if !fileManager.fileExists(atPath: location) { if !fileManager.fileExists(atPath: location) {
@@ -600,8 +634,6 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
} }
// let sortedCollection = collection.sorted(by: { $0.absoluteString < $1.absoluteString })
// Calculate file details (sizes and icons) // Calculate file details (sizes and icons)
var fileSize: [URL: Int64] = [:] var fileSize: [URL: Int64] = [:]
var fileIcon: [URL: NSImage?] = [:] var fileIcon: [URL: NSImage?] = [:]
@@ -640,10 +672,6 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
// Move files to trash using applescript/Finder so it asks for user password if needed // Move files to trash using applescript/Finder so it asks for user password if needed
func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {}) { func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {}) {
@AppStorage("settings.sentinel.enable") var sentinel: Bool = false @AppStorage("settings.sentinel.enable") var sentinel: Bool = false
if sentinel {
launchctl(load: false)
}
var filesFinder = fileURLs var filesFinder = fileURLs
var filesSudo: [URL] = [] var filesSudo: [URL] = []
@@ -689,107 +717,6 @@ func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {})
} }
} }
//func moveFilesToTrash(at fileURLs: [URL] = [], appState: AppState, undo: Bool = false, completion: @escaping () -> Void = {}) {
// @AppStorage("settings.sentinel.enable") var sentinel: Bool = false
// if sentinel {
// launchctl(load: false)
// }
//
// let home = FileManager.default.homeDirectoryForCurrentUser.path
//
// if undo {
// for url in appState.trashedFiles {
// let file = url.lastPathComponent
// let destinationURL = url.deletingLastPathComponent()
//
// let destinationPath = destinationURL.path
// let trashPath = "\(home)/.Trash/\(file)"
//
// if !destinationPath.starts(with: home) {
// executePrivilegedCommand(launchPath: "/bin/cp", arguments: ["-R"] + [trashPath, destinationPath])
// } else {
// let task = Process()
// task.launchPath = "/bin/cp"
// task.arguments = ["-R"] + [trashPath, destinationPath]
//
// let pipe = Pipe()
// task.standardOutput = pipe
// task.launch()
//
// task.waitUntilExit()
// }
// }
//
// AudioServicesPlaySystemSound(0xf);
//
// updateOnMain {
// appState.trashedFiles.removeAll()
// }
//
// } else {
// let privilegedURLs = fileURLs.filter { !$0.path.starts(with: home) }
//
// if !privilegedURLs.isEmpty {
// let filePaths = fileURLs.map { $0.path }
// executePrivilegedCommand(launchPath: "/bin/mv", arguments: ["-f"] + filePaths + ["\(home)/.Trash"])
// } else {
// let filePaths = fileURLs.map { $0.path }
// let task = Process()
// task.launchPath = "/bin/mv"
// task.arguments = ["-f"] + filePaths + ["\(home)/.Trash"]
//
// let pipe = Pipe()
// task.standardOutput = pipe
// task.launch()
//
// task.waitUntilExit()
// }
//
// AudioServicesPlaySystemSound(0x10);
//
// }
//
// completion()
//}
// Audio
//drag to trash.aif
//AudioServicesPlaySystemSound(0x10);
//poof item of dock.aif
//AudioServicesPlaySystemSound(0xf);
//func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {}) {
// @AppStorage("settings.sentinel.enable") var sentinel: Bool = false
// if sentinel {
// launchctl(load: false)
// }
//
// let privilegedURLs = fileURLs.filter { !$0.path.starts(with: home) }
//
// if !privilegedURLs.isEmpty {
// print("Found system files")
// // Use privileged sudo mv function
// let filePaths = privilegedURLs.map { $0.path }
// executePrivilegedCommand(launchPath: "/bin/mv", arguments: filePaths + ["\(home)/.Trash"])
// completion()
// } else {
// // Use regular shell process for files within the user's home directory
// let filePaths = fileURLs.map { $0.path }
// let task = Process()
// task.launchPath = "/bin/mv"
// task.arguments = filePaths + ["\(home)/.Trash"]
//
// let pipe = Pipe()
// task.standardOutput = pipe
// task.launch()
//
// task.waitUntilExit()
//
// completion()
// }
//}
func isSocketFile(at url: URL) -> Bool { func isSocketFile(at url: URL) -> Bool {
do { do {
@@ -804,8 +731,6 @@ func isSocketFile(at url: URL) -> Bool {
} }
// Undo trash action // Undo trash action
func undoTrash(appState: AppState, completion: @escaping () -> Void = {}) { func undoTrash(appState: AppState, completion: @escaping () -> Void = {}) {
let scriptSource = """ let scriptSource = """
@@ -844,55 +769,3 @@ func undoTrash(appState: AppState, completion: @escaping () -> Void = {}) {
} }
//func isAppRunning(appName: String) -> Bool {
// let task = Process()
// task.executableURL = URL(fileURLWithPath: "/usr/bin/pgrep")
// task.arguments = ["-af", appName]
//
// let outputPipe = Pipe()
// task.standardOutput = outputPipe
//
// do {
// try task.run()
// } catch {
// printOS("Failed to run task: \(error)")
// return false
// }
//
// let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
// let output = String(decoding: outputData, as: UTF8.self)
//
// if output.isEmpty {
// return false
// } else {
// return true
// }
//}
//
//// Kill app if running
//func killApp2(appName: String, completion: @escaping () -> Void = {}) {
// if isAppRunning(appName: appName) {
// let task = Process()
// task.executableURL = URL(fileURLWithPath: "/usr/bin/pkill")
// task.arguments = ["-af", appName]
//
// do {
// try task.run()
// } catch {
// printOS("Failed to run task: \(error)")
// return
// }
//
// }
// completion()
//}
-599
View File
@@ -1,599 +0,0 @@
////
//// Shapes.swift
//// Pearcleaner
////
//// Created by Alin Lupascu on 11/11/23.
////
//
//import Foundation
//import SwiftUI
//
//// Pear Logo
//struct Pear: Shape {
// func path(in rect: CGRect) -> Path {
// var path = Path()
// let width = rect.size.width
// let height = rect.size.height
// path.move(to: CGPoint(x: 0.5985*width, y: 0.045*height))
// path.addCurve(to: CGPoint(x: 0.508*width, y: 0.141*height), control1: CGPoint(x: 0.571*width, y: 0.0635*height), control2: CGPoint(x: 0.521*width, y: 0.116*height))
// path.addLine(to: CGPoint(x: 0.4985*width, y: 0.159*height))
// path.addLine(to: CGPoint(x: 0.4725*width, y: 0.1325*height))
// path.addCurve(to: CGPoint(x: 0.318*width, y: 0.0975*height), control1: CGPoint(x: 0.433*width, y: 0.092*height), control2: CGPoint(x: 0.383*width, y: 0.0805*height))
// path.addCurve(to: CGPoint(x: 0.2105*width, y: 0.15*height), control1: CGPoint(x: 0.2875*width, y: 0.1055*height), control2: CGPoint(x: 0.231*width, y: 0.133*height))
// path.addLine(to: CGPoint(x: 0.1985*width, y: 0.16*height))
// path.addLine(to: CGPoint(x: 0.2125*width, y: 0.18*height))
// path.addCurve(to: CGPoint(x: 0.316*width, y: 0.273*height), control1: CGPoint(x: 0.2335*width, y: 0.2105*height), control2: CGPoint(x: 0.287*width, y: 0.2585*height))
// path.addCurve(to: CGPoint(x: 0.371*width, y: 0.289*height), control1: CGPoint(x: 0.331*width, y: 0.2805*height), control2: CGPoint(x: 0.354*width, y: 0.287*height))
// path.addLine(to: CGPoint(x: 0.4*width, y: 0.2915*height))
// path.addLine(to: CGPoint(x: 0.375*width, y: 0.318*height))
// path.addCurve(to: CGPoint(x: 0.3195*width, y: 0.443*height), control1: CGPoint(x: 0.3415*width, y: 0.3535*height), control2: CGPoint(x: 0.326*width, y: 0.388*height))
// path.addCurve(to: CGPoint(x: 0.2635*width, y: 0.564*height), control1: CGPoint(x: 0.3125*width, y: 0.501*height), control2: CGPoint(x: 0.302*width, y: 0.5235*height))
// path.addCurve(to: CGPoint(x: 0.19*width, y: 0.7345*height), control1: CGPoint(x: 0.21*width, y: 0.62*height), control2: CGPoint(x: 0.19*width, y: 0.6665*height))
// path.addCurve(to: CGPoint(x: 0.3235*width, y: 0.949*height), control1: CGPoint(x: 0.19*width, y: 0.8305*height), control2: CGPoint(x: 0.2385*width, y: 0.9085*height))
// path.addCurve(to: CGPoint(x: 0.4125*width, y: 0.9675*height), control1: CGPoint(x: 0.3605*width, y: 0.9665*height), control2: CGPoint(x: 0.365*width, y: 0.9675*height))
// path.addCurve(to: CGPoint(x: 0.4875*width, y: 0.958*height), control1: CGPoint(x: 0.449*width, y: 0.9675*height), control2: CGPoint(x: 0.4695*width, y: 0.965*height))
// path.addCurve(to: CGPoint(x: 0.547*width, y: 0.9595*height), control1: CGPoint(x: 0.5125*width, y: 0.949*height), control2: CGPoint(x: 0.513*width, y: 0.949*height))
// path.addCurve(to: CGPoint(x: 0.759*width, y: 0.9135*height), control1: CGPoint(x: 0.6225*width, y: 0.983*height), control2: CGPoint(x: 0.702*width, y: 0.9655*height))
// path.addCurve(to: CGPoint(x: 0.7975*width, y: 0.817*height), control1: CGPoint(x: 0.8085*width, y: 0.8675*height), control2: CGPoint(x: 0.8225*width, y: 0.8335*height))
// path.addCurve(to: CGPoint(x: 0.7615*width, y: 0.817*height), control1: CGPoint(x: 0.786*width, y: 0.8095*height), control2: CGPoint(x: 0.7835*width, y: 0.8095*height))
// path.addCurve(to: CGPoint(x: 0.661*width, y: 0.8015*height), control1: CGPoint(x: 0.723*width, y: 0.83*height), control2: CGPoint(x: 0.6935*width, y: 0.8255*height))
// path.addCurve(to: CGPoint(x: 0.634*width, y: 0.6745*height), control1: CGPoint(x: 0.6245*width, y: 0.7745*height), control2: CGPoint(x: 0.611*width, y: 0.711*height))
// path.addCurve(to: CGPoint(x: 0.6545*width, y: 0.618*height), control1: CGPoint(x: 0.659*width, y: 0.6345*height), control2: CGPoint(x: 0.6605*width, y: 0.631*height))
// path.addCurve(to: CGPoint(x: 0.6325*width, y: 0.6*height), control1: CGPoint(x: 0.651*width, y: 0.611*height), control2: CGPoint(x: 0.6415*width, y: 0.603*height))
// path.addCurve(to: CGPoint(x: 0.575*width, y: 0.5175*height), control1: CGPoint(x: 0.603*width, y: 0.59*height), control2: CGPoint(x: 0.575*width, y: 0.55*height))
// path.addCurve(to: CGPoint(x: 0.6595*width, y: 0.4255*height), control1: CGPoint(x: 0.575*width, y: 0.4685*height), control2: CGPoint(x: 0.609*width, y: 0.4315*height))
// path.addCurve(to: CGPoint(x: 0.691*width, y: 0.4095*height), control1: CGPoint(x: 0.6755*width, y: 0.4235*height), control2: CGPoint(x: 0.684*width, y: 0.419*height))
// path.addCurve(to: CGPoint(x: 0.691*width, y: 0.3745*height), control1: CGPoint(x: 0.701*width, y: 0.3965*height), control2: CGPoint(x: 0.701*width, y: 0.396*height))
// path.addCurve(to: CGPoint(x: 0.565*width, y: 0.2645*height), control1: CGPoint(x: 0.667*width, y: 0.3215*height), control2: CGPoint(x: 0.6175*width, y: 0.278*height))
// path.addLine(to: CGPoint(x: 0.54*width, y: 0.258*height))
// path.addLine(to: CGPoint(x: 0.54*width, y: 0.2355*height))
// path.addCurve(to: CGPoint(x: 0.6215*width, y: 0.102*height), control1: CGPoint(x: 0.54*width, y: 0.192*height), control2: CGPoint(x: 0.5745*width, y: 0.1355*height))
// path.addCurve(to: CGPoint(x: 0.66*width, y: 0.0575*height), control1: CGPoint(x: 0.653*width, y: 0.0795*height), control2: CGPoint(x: 0.66*width, y: 0.0715*height))
// path.addCurve(to: CGPoint(x: 0.6315*width, y: 0.03*height), control1: CGPoint(x: 0.66*width, y: 0.044*height), control2: CGPoint(x: 0.6455*width, y: 0.03*height))
// path.addCurve(to: CGPoint(x: 0.5985*width, y: 0.045*height), control1: CGPoint(x: 0.626*width, y: 0.03*height), control2: CGPoint(x: 0.6115*width, y: 0.037*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.4155*width, y: 0.1615*height))
// path.addCurve(to: CGPoint(x: 0.444*width, y: 0.19*height), control1: CGPoint(x: 0.4245*width, y: 0.1675*height), control2: CGPoint(x: 0.4375*width, y: 0.1805*height))
// path.addLine(to: CGPoint(x: 0.4555*width, y: 0.2075*height))
// path.addLine(to: CGPoint(x: 0.445*width, y: 0.216*height))
// path.addCurve(to: CGPoint(x: 0.369*width, y: 0.23*height), control1: CGPoint(x: 0.4265*width, y: 0.231*height), control2: CGPoint(x: 0.397*width, y: 0.2365*height))
// path.addCurve(to: CGPoint(x: 0.285*width, y: 0.175*height), control1: CGPoint(x: 0.345*width, y: 0.224*height), control2: CGPoint(x: 0.285*width, y: 0.185*height))
// path.addCurve(to: CGPoint(x: 0.2865*width, y: 0.17*height), control1: CGPoint(x: 0.285*width, y: 0.1725*height), control2: CGPoint(x: 0.2855*width, y: 0.17*height))
// path.addCurve(to: CGPoint(x: 0.315*width, y: 0.16*height), control1: CGPoint(x: 0.287*width, y: 0.17*height), control2: CGPoint(x: 0.3*width, y: 0.1655*height))
// path.addCurve(to: CGPoint(x: 0.4155*width, y: 0.1615*height), control1: CGPoint(x: 0.3535*width, y: 0.1465*height), control2: CGPoint(x: 0.394*width, y: 0.147*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.553*width, y: 0.3225*height))
// path.addCurve(to: CGPoint(x: 0.62*width, y: 0.369*height), control1: CGPoint(x: 0.5755*width, y: 0.329*height), control2: CGPoint(x: 0.62*width, y: 0.3605*height))
// path.addCurve(to: CGPoint(x: 0.599*width, y: 0.382*height), control1: CGPoint(x: 0.62*width, y: 0.371*height), control2: CGPoint(x: 0.6105*width, y: 0.377*height))
// path.addCurve(to: CGPoint(x: 0.5295*width, y: 0.453*height), control1: CGPoint(x: 0.572*width, y: 0.3945*height), control2: CGPoint(x: 0.5445*width, y: 0.4225*height))
// path.addCurve(to: CGPoint(x: 0.5315*width, y: 0.5815*height), control1: CGPoint(x: 0.512*width, y: 0.4895*height), control2: CGPoint(x: 0.5125*width, y: 0.5425*height))
// path.addCurve(to: CGPoint(x: 0.565*width, y: 0.626*height), control1: CGPoint(x: 0.5395*width, y: 0.597*height), control2: CGPoint(x: 0.5545*width, y: 0.6175*height))
// path.addCurve(to: CGPoint(x: 0.576*width, y: 0.6575*height), control1: CGPoint(x: 0.584*width, y: 0.642*height), control2: CGPoint(x: 0.584*width, y: 0.6425*height))
// path.addCurve(to: CGPoint(x: 0.572*width, y: 0.7725*height), control1: CGPoint(x: 0.5645*width, y: 0.68*height), control2: CGPoint(x: 0.562*width, y: 0.744*height))
// path.addCurve(to: CGPoint(x: 0.6785*width, y: 0.8755*height), control1: CGPoint(x: 0.5875*width, y: 0.8195*height), control2: CGPoint(x: 0.632*width, y: 0.862*height))
// path.addCurve(to: CGPoint(x: 0.6665*width, y: 0.902*height), control1: CGPoint(x: 0.7045*width, y: 0.883*height), control2: CGPoint(x: 0.701*width, y: 0.8905*height))
// path.addCurve(to: CGPoint(x: 0.546*width, y: 0.897*height), control1: CGPoint(x: 0.6305*width, y: 0.9145*height), control2: CGPoint(x: 0.585*width, y: 0.9125*height))
// path.addCurve(to: CGPoint(x: 0.498*width, y: 0.89*height), control1: CGPoint(x: 0.517*width, y: 0.885*height), control2: CGPoint(x: 0.5115*width, y: 0.8845*height))
// path.addCurve(to: CGPoint(x: 0.405*width, y: 0.909*height), control1: CGPoint(x: 0.451*width, y: 0.909*height), control2: CGPoint(x: 0.4415*width, y: 0.911*height))
// path.addCurve(to: CGPoint(x: 0.2925*width, y: 0.8535*height), control1: CGPoint(x: 0.3585*width, y: 0.9065*height), control2: CGPoint(x: 0.3245*width, y: 0.8895*height))
// path.addCurve(to: CGPoint(x: 0.2485*width, y: 0.734*height), control1: CGPoint(x: 0.2625*width, y: 0.8205*height), control2: CGPoint(x: 0.2485*width, y: 0.7825*height))
// path.addCurve(to: CGPoint(x: 0.3115*width, y: 0.597*height), control1: CGPoint(x: 0.2485*width, y: 0.6825*height), control2: CGPoint(x: 0.267*width, y: 0.642*height))
// path.addCurve(to: CGPoint(x: 0.375*width, y: 0.465*height), control1: CGPoint(x: 0.3535*width, y: 0.5545*height), control2: CGPoint(x: 0.3695*width, y: 0.5215*height))
// path.addCurve(to: CGPoint(x: 0.385*width, y: 0.4075*height), control1: CGPoint(x: 0.3775*width, y: 0.441*height), control2: CGPoint(x: 0.382*width, y: 0.415*height))
// path.addCurve(to: CGPoint(x: 0.438*width, y: 0.3395*height), control1: CGPoint(x: 0.394*width, y: 0.3835*height), control2: CGPoint(x: 0.4195*width, y: 0.351*height))
// path.addCurve(to: CGPoint(x: 0.553*width, y: 0.3225*height), control1: CGPoint(x: 0.4785*width, y: 0.3145*height), control2: CGPoint(x: 0.5105*width, y: 0.31*height))
// path.closeSubpath()
// return path
// }
//}
//
//// Pear text
//struct Pearcleaner: Shape {
// func path(in rect: CGRect) -> Path {
// var path = Path()
// let width = rect.size.width
// let height = rect.size.height
// path.move(to: CGPoint(x: 1.10767*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.10292*width, y: -0.22248*height), control1: CGPoint(x: 1.10519*width, y: -0.23943*height), control2: CGPoint(x: 1.10375*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.09362*width, y: -0.14302*height), control1: CGPoint(x: 1.10044*width, y: -0.1854*height), control2: CGPoint(x: 1.09755*width, y: -0.16315*height))
// path.addCurve(to: CGPoint(x: 1.0744*width, y: -0.09005*height), control1: CGPoint(x: 1.08783*width, y: -0.11442*height), control2: CGPoint(x: 1.07977*width, y: -0.09005*height))
// path.addCurve(to: CGPoint(x: 1.09052*width, y: -0.24684*height), control1: CGPoint(x: 1.07977*width, y: -0.10594*height), control2: CGPoint(x: 1.08928*width, y: -0.16103*height))
// path.addCurve(to: CGPoint(x: 1.05869*width, y: -0.43648*height), control1: CGPoint(x: 1.09217*width, y: -0.36126*height), control2: CGPoint(x: 1.0775*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.02955*width, y: -0.36868*height), control1: CGPoint(x: 1.04546*width, y: -0.43648*height), control2: CGPoint(x: 1.03513*width, y: -0.39834*height))
// path.addLine(to: CGPoint(x: 1.03058*width, y: -0.40364*height))
// path.addCurve(to: CGPoint(x: 1.02686*width, y: -0.42377*height), control1: CGPoint(x: 1.031*width, y: -0.41529*height), control2: CGPoint(x: 1.02934*width, y: -0.42377*height))
// path.addLine(to: CGPoint(x: 1.01054*width, y: -0.42377*height))
// path.addCurve(to: CGPoint(x: 1.00578*width, y: -0.40258*height), control1: CGPoint(x: 1.00805*width, y: -0.42377*height), control2: CGPoint(x: 1.00619*width, y: -0.41529*height))
// path.addLine(to: CGPoint(x: 0.98532*width, y: 0.26062*height))
// path.addCurve(to: CGPoint(x: 0.98883*width, y: 0.2818*height), control1: CGPoint(x: 0.98491*width, y: 0.27227*height), control2: CGPoint(x: 0.98656*width, y: 0.2818*height))
// path.addLine(to: CGPoint(x: 1.00516*width, y: 0.2818*height))
// path.addCurve(to: CGPoint(x: 1.01012*width, y: 0.25956*height), control1: CGPoint(x: 1.00805*width, y: 0.2818*height), control2: CGPoint(x: 1.00971*width, y: 0.27333*height))
// path.addLine(to: CGPoint(x: 1.02087*width, y: -0.09111*height))
// path.addCurve(to: CGPoint(x: 1.05146*width, y: -0.31888*height), control1: CGPoint(x: 1.02583*width, y: -0.2532*height), control2: CGPoint(x: 1.03926*width, y: -0.31888*height))
// path.addCurve(to: CGPoint(x: 1.06282*width, y: -0.20023*height), control1: CGPoint(x: 1.06262*width, y: -0.31888*height), control2: CGPoint(x: 1.06572*width, y: -0.26379*height))
// path.addCurve(to: CGPoint(x: 1.04298*width, y: -0.09429*height), control1: CGPoint(x: 1.06034*width, y: -0.14302*height), control2: CGPoint(x: 1.05352*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.02976*width, y: -0.09111*height), control1: CGPoint(x: 1.03203*width, y: -0.10594*height), control2: CGPoint(x: 1.03058*width, y: -0.1017*height))
// path.addLine(to: CGPoint(x: 1.02562*width, y: -0.04132*height))
// path.addCurve(to: CGPoint(x: 1.03017*width, y: -0.00848*height), control1: CGPoint(x: 1.02438*width, y: -0.02649*height), control2: CGPoint(x: 1.02542*width, y: -0.01801*height))
// path.addCurve(to: CGPoint(x: 1.05538*width, y: 0.01271*height), control1: CGPoint(x: 1.03348*width, y: -0.00106*height), control2: CGPoint(x: 1.04154*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.11801*width, y: -0.19917*height), control1: CGPoint(x: 1.08411*width, y: 0.01271*height), control2: CGPoint(x: 1.10891*width, y: -0.05615*height))
// path.addCurve(to: CGPoint(x: 1.11573*width, y: -0.22248*height), control1: CGPoint(x: 1.11883*width, y: -0.21188*height), control2: CGPoint(x: 1.1178*width, y: -0.2193*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.18208*width, y: -0.214*height))
// path.addCurve(to: CGPoint(x: 1.14695*width, y: -0.09641*height), control1: CGPoint(x: 1.17423*width, y: -0.13137*height), control2: CGPoint(x: 1.15956*width, y: -0.09641*height))
// path.addCurve(to: CGPoint(x: 1.12959*width, y: -0.14302*height), control1: CGPoint(x: 1.13806*width, y: -0.09641*height), control2: CGPoint(x: 1.13248*width, y: -0.11442*height))
// path.addCurve(to: CGPoint(x: 1.17712*width, y: -0.28604*height), control1: CGPoint(x: 1.14757*width, y: -0.1409*height), control2: CGPoint(x: 1.17237*width, y: -0.18434*height))
// path.addCurve(to: CGPoint(x: 1.14984*width, y: -0.43648*height), control1: CGPoint(x: 1.18126*width, y: -0.37186*height), control2: CGPoint(x: 1.16844*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.10148*width, y: -0.23095*height), control1: CGPoint(x: 1.12918*width, y: -0.43648*height), control2: CGPoint(x: 1.10644*width, y: -0.35702*height))
// path.addCurve(to: CGPoint(x: 1.1424*width, y: 0.01271*height), control1: CGPoint(x: 1.0959*width, y: -0.09323*height), control2: CGPoint(x: 1.11533*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.198*width, y: -0.19917*height), control1: CGPoint(x: 1.16824*width, y: 0.01271*height), control2: CGPoint(x: 1.18973*width, y: -0.08263*height))
// path.addCurve(to: CGPoint(x: 1.19573*width, y: -0.22248*height), control1: CGPoint(x: 1.19883*width, y: -0.21082*height), control2: CGPoint(x: 1.19821*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.18766*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.18208*width, y: -0.214*height), control1: CGPoint(x: 1.18518*width, y: -0.23943*height), control2: CGPoint(x: 1.18415*width, y: -0.23519*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.12773*width, y: -0.20659*height))
// path.addCurve(to: CGPoint(x: 1.12794*width, y: -0.214*height), control1: CGPoint(x: 1.12773*width, y: -0.20871*height), control2: CGPoint(x: 1.12794*width, y: -0.21188*height))
// path.addCurve(to: CGPoint(x: 1.14922*width, y: -0.32736*height), control1: CGPoint(x: 1.13021*width, y: -0.27863*height), control2: CGPoint(x: 1.14137*width, y: -0.32736*height))
// path.addCurve(to: CGPoint(x: 1.15563*width, y: -0.2977*height), control1: CGPoint(x: 1.15336*width, y: -0.32736*height), control2: CGPoint(x: 1.15563*width, y: -0.31465*height))
// path.addCurve(to: CGPoint(x: 1.12773*width, y: -0.20659*height), control1: CGPoint(x: 1.15563*width, y: -0.25426*height), control2: CGPoint(x: 1.14178*width, y: -0.20976*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.21075*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.24237*width, y: -0.0678*height), control1: CGPoint(x: 1.22604*width, y: 0.01271*height), control2: CGPoint(x: 1.23782*width, y: -0.0339*height))
// path.addCurve(to: CGPoint(x: 1.26552*width, y: 0.01271*height), control1: CGPoint(x: 1.24547*width, y: -0.01907*height), control2: CGPoint(x: 1.25373*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.30644*width, y: -0.19917*height), control1: CGPoint(x: 1.28866*width, y: 0.01271*height), control2: CGPoint(x: 1.30189*width, y: -0.11018*height))
// path.addCurve(to: CGPoint(x: 1.30416*width, y: -0.22248*height), control1: CGPoint(x: 1.30706*width, y: -0.21082*height), control2: CGPoint(x: 1.30664*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.2961*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.29135*width, y: -0.22248*height), control1: CGPoint(x: 1.29362*width, y: -0.23943*height), control2: CGPoint(x: 1.29218*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.27192*width, y: -0.09429*height), control1: CGPoint(x: 1.28846*width, y: -0.18328*height), control2: CGPoint(x: 1.28143*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.26717*width, y: -0.18434*height), control1: CGPoint(x: 1.26614*width, y: -0.09429*height), control2: CGPoint(x: 1.26552*width, y: -0.12819*height))
// path.addLine(to: CGPoint(x: 1.27378*width, y: -0.40258*height))
// path.addCurve(to: CGPoint(x: 1.27027*width, y: -0.42377*height), control1: CGPoint(x: 1.2742*width, y: -0.41423*height), control2: CGPoint(x: 1.27275*width, y: -0.42377*height))
// path.addLine(to: CGPoint(x: 1.25373*width, y: -0.42377*height))
// path.addCurve(to: CGPoint(x: 1.24898*width, y: -0.40258*height), control1: CGPoint(x: 1.25125*width, y: -0.42377*height), control2: CGPoint(x: 1.24939*width, y: -0.41529*height))
// path.addLine(to: CGPoint(x: 1.24877*width, y: -0.39622*height))
// path.addCurve(to: CGPoint(x: 1.22769*width, y: -0.43648*height), control1: CGPoint(x: 1.24547*width, y: -0.41105*height), control2: CGPoint(x: 1.23885*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.18264*width, y: -0.24473*height), control1: CGPoint(x: 1.20765*width, y: -0.43648*height), control2: CGPoint(x: 1.18925*width, y: -0.35596*height))
// path.addCurve(to: CGPoint(x: 1.21075*width, y: 0.01271*height), control1: CGPoint(x: 1.1752*width, y: -0.11865*height), control2: CGPoint(x: 1.18512*width, y: 0.01271*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.24567*width, y: -0.29346*height))
// path.addCurve(to: CGPoint(x: 1.21901*width, y: -0.09429*height), control1: CGPoint(x: 1.24278*width, y: -0.18858*height), control2: CGPoint(x: 1.23183*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.20806*width, y: -0.21294*height), control1: CGPoint(x: 1.2093*width, y: -0.09429*height), control2: CGPoint(x: 1.20496*width, y: -0.15044*height))
// path.addCurve(to: CGPoint(x: 1.23245*width, y: -0.3263*height), control1: CGPoint(x: 1.21178*width, y: -0.28604*height), control2: CGPoint(x: 1.22232*width, y: -0.3263*height))
// path.addCurve(to: CGPoint(x: 1.24567*width, y: -0.29346*height), control1: CGPoint(x: 1.23968*width, y: -0.3263*height), control2: CGPoint(x: 1.24361*width, y: -0.30511*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.36936*width, y: -0.22248*height))
// path.addCurve(to: CGPoint(x: 1.34807*width, y: -0.09429*height), control1: CGPoint(x: 1.36626*width, y: -0.17904*height), control2: CGPoint(x: 1.35799*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.34228*width, y: -0.16527*height), control1: CGPoint(x: 1.34331*width, y: -0.09429*height), control2: CGPoint(x: 1.34001*width, y: -0.11442*height))
// path.addCurve(to: CGPoint(x: 1.34972*width, y: -0.31253*height), control1: CGPoint(x: 1.34497*width, y: -0.22777*height), control2: CGPoint(x: 1.34931*width, y: -0.30405*height))
// path.addCurve(to: CGPoint(x: 1.34393*width, y: -0.38245*height), control1: CGPoint(x: 1.35117*width, y: -0.34749*height), control2: CGPoint(x: 1.35075*width, y: -0.36656*height))
// path.addCurve(to: CGPoint(x: 1.315*width, y: -0.44813*height), control1: CGPoint(x: 1.3336*width, y: -0.40576*height), control2: CGPoint(x: 1.32099*width, y: -0.42589*height))
// path.addCurve(to: CGPoint(x: 1.30322*width, y: -0.52017*height), control1: CGPoint(x: 1.31541*width, y: -0.48945*height), control2: CGPoint(x: 1.31128*width, y: -0.52441*height))
// path.addCurve(to: CGPoint(x: 1.29495*width, y: -0.40258*height), control1: CGPoint(x: 1.29392*width, y: -0.51594*height), control2: CGPoint(x: 1.28999*width, y: -0.46508*height))
// path.addCurve(to: CGPoint(x: 1.29061*width, y: -0.21506*height), control1: CGPoint(x: 1.29619*width, y: -0.3655*height), control2: CGPoint(x: 1.29537*width, y: -0.27969*height))
// path.addCurve(to: CGPoint(x: 1.29206*width, y: -0.19493*height), control1: CGPoint(x: 1.28979*width, y: -0.20553*height), control2: CGPoint(x: 1.2902*width, y: -0.19811*height))
// path.addLine(to: CGPoint(x: 1.30177*width, y: -0.17586*height))
// path.addCurve(to: CGPoint(x: 1.30549*width, y: -0.18434*height), control1: CGPoint(x: 1.30363*width, y: -0.17268*height), control2: CGPoint(x: 1.30487*width, y: -0.17374*height))
// path.addCurve(to: CGPoint(x: 1.31211*width, y: -0.35279*height), control1: CGPoint(x: 1.30859*width, y: -0.23519*height), control2: CGPoint(x: 1.31087*width, y: -0.29346*height))
// path.addLine(to: CGPoint(x: 1.32017*width, y: -0.33372*height))
// path.addCurve(to: CGPoint(x: 1.32347*width, y: -0.29664*height), control1: CGPoint(x: 1.32347*width, y: -0.3263*height), control2: CGPoint(x: 1.32451*width, y: -0.31783*height))
// path.addCurve(to: CGPoint(x: 1.31583*width, y: -0.14408*height), control1: CGPoint(x: 1.32079*width, y: -0.23625*height), control2: CGPoint(x: 1.31748*width, y: -0.19387*height))
// path.addCurve(to: CGPoint(x: 1.3429*width, y: 0.01271*height), control1: CGPoint(x: 1.31335*width, y: -0.06145*height), control2: CGPoint(x: 1.32554*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.38444*width, y: -0.19917*height), control1: CGPoint(x: 1.36543*width, y: 0.01271*height), control2: CGPoint(x: 1.3799*width, y: -0.11018*height))
// path.addCurve(to: CGPoint(x: 1.38217*width, y: -0.22248*height), control1: CGPoint(x: 1.38506*width, y: -0.21082*height), control2: CGPoint(x: 1.38465*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.37411*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.36936*width, y: -0.22248*height), control1: CGPoint(x: 1.37163*width, y: -0.23943*height), control2: CGPoint(x: 1.37018*width, y: -0.23519*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.45862*width, y: -0.22248*height))
// path.addLine(to: CGPoint(x: 1.45056*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.44539*width, y: -0.21824*height), control1: CGPoint(x: 1.44787*width, y: -0.23943*height), control2: CGPoint(x: 1.44705*width, y: -0.23413*height))
// path.addCurve(to: CGPoint(x: 1.41253*width, y: -0.09429*height), control1: CGPoint(x: 1.4363*width, y: -0.13666*height), control2: CGPoint(x: 1.42514*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.39455*width, y: -0.22036*height), control1: CGPoint(x: 1.39703*width, y: -0.09429*height), control2: CGPoint(x: 1.39166*width, y: -0.15573*height))
// path.addCurve(to: CGPoint(x: 1.41956*width, y: -0.32948*height), control1: CGPoint(x: 1.39744*width, y: -0.28498*height), control2: CGPoint(x: 1.40778*width, y: -0.32948*height))
// path.addCurve(to: CGPoint(x: 1.42968*width, y: -0.31783*height), control1: CGPoint(x: 1.42348*width, y: -0.32948*height), control2: CGPoint(x: 1.427*width, y: -0.32418*height))
// path.addCurve(to: CGPoint(x: 1.43506*width, y: -0.32312*height), control1: CGPoint(x: 1.43216*width, y: -0.31253*height), control2: CGPoint(x: 1.4332*width, y: -0.31253*height))
// path.addLine(to: CGPoint(x: 1.44126*width, y: -0.3602*height))
// path.addCurve(to: CGPoint(x: 1.44043*width, y: -0.38987*height), control1: CGPoint(x: 1.44271*width, y: -0.36974*height), control2: CGPoint(x: 1.44312*width, y: -0.37609*height))
// path.addCurve(to: CGPoint(x: 1.4148*width, y: -0.43648*height), control1: CGPoint(x: 1.43568*width, y: -0.41211*height), control2: CGPoint(x: 1.42576*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.36768*width, y: -0.2246*height), control1: CGPoint(x: 1.39455*width, y: -0.43648*height), control2: CGPoint(x: 1.37202*width, y: -0.3549*height))
// path.addCurve(to: CGPoint(x: 1.40592*width, y: 0.01271*height), control1: CGPoint(x: 1.36355*width, y: -0.09535*height), control2: CGPoint(x: 1.37926*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.46089*width, y: -0.19917*height), control1: CGPoint(x: 1.42968*width, y: 0.01271*height), control2: CGPoint(x: 1.45139*width, y: -0.0731*height))
// path.addCurve(to: CGPoint(x: 1.45862*width, y: -0.22248*height), control1: CGPoint(x: 1.46172*width, y: -0.21082*height), control2: CGPoint(x: 1.4611*width, y: -0.21824*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.52396*width, y: -0.22354*height))
// path.addLine(to: CGPoint(x: 1.5159*width, y: -0.23625*height))
// path.addCurve(to: CGPoint(x: 1.51114*width, y: -0.22566*height), control1: CGPoint(x: 1.51362*width, y: -0.23943*height), control2: CGPoint(x: 1.51218*width, y: -0.23731*height))
// path.addCurve(to: CGPoint(x: 1.48428*width, y: -0.09641*height), control1: CGPoint(x: 1.50164*width, y: -0.12395*height), control2: CGPoint(x: 1.4911*width, y: -0.09641*height))
// path.addCurve(to: CGPoint(x: 1.46692*width, y: -0.25214*height), control1: CGPoint(x: 1.47312*width, y: -0.09641*height), control2: CGPoint(x: 1.46754*width, y: -0.17374*height))
// path.addCurve(to: CGPoint(x: 1.51301*width, y: -0.44284*height), control1: CGPoint(x: 1.48325*width, y: -0.27333*height), control2: CGPoint(x: 1.50267*width, y: -0.33372*height))
// path.addCurve(to: CGPoint(x: 1.49813*width, y: -0.70133*height), control1: CGPoint(x: 1.52231*width, y: -0.54242*height), control2: CGPoint(x: 1.52603*width, y: -0.70133*height))
// path.addCurve(to: CGPoint(x: 1.44584*width, y: -0.36762*height), control1: CGPoint(x: 1.47705*width, y: -0.70133*height), control2: CGPoint(x: 1.45452*width, y: -0.57314*height))
// path.addCurve(to: CGPoint(x: 1.47829*width, y: 0.01271*height), control1: CGPoint(x: 1.43922*width, y: -0.21188*height), control2: CGPoint(x: 1.44294*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.52499*width, y: -0.18964*height), control1: CGPoint(x: 1.49854*width, y: 0.01271*height), control2: CGPoint(x: 1.51569*width, y: -0.06145*height))
// path.addCurve(to: CGPoint(x: 1.52396*width, y: -0.22354*height), control1: CGPoint(x: 1.52665*width, y: -0.21082*height), control2: CGPoint(x: 1.52685*width, y: -0.21824*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.46878*width, y: -0.34961*height))
// path.addCurve(to: CGPoint(x: 1.49523*width, y: -0.59221*height), control1: CGPoint(x: 1.47394*width, y: -0.47462*height), control2: CGPoint(x: 1.488*width, y: -0.5901*height))
// path.addCurve(to: CGPoint(x: 1.49296*width, y: -0.46508*height), control1: CGPoint(x: 1.5006*width, y: -0.59327*height), control2: CGPoint(x: 1.49957*width, y: -0.52759*height))
// path.addCurve(to: CGPoint(x: 1.46878*width, y: -0.34961*height), control1: CGPoint(x: 1.48655*width, y: -0.40258*height), control2: CGPoint(x: 1.47684*width, y: -0.36444*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.58615*width, y: -0.214*height))
// path.addCurve(to: CGPoint(x: 1.55101*width, y: -0.09641*height), control1: CGPoint(x: 1.5783*width, y: -0.13137*height), control2: CGPoint(x: 1.56362*width, y: -0.09641*height))
// path.addCurve(to: CGPoint(x: 1.53366*width, y: -0.14302*height), control1: CGPoint(x: 1.54213*width, y: -0.09641*height), control2: CGPoint(x: 1.53655*width, y: -0.11442*height))
// path.addCurve(to: CGPoint(x: 1.58119*width, y: -0.28604*height), control1: CGPoint(x: 1.55164*width, y: -0.1409*height), control2: CGPoint(x: 1.57644*width, y: -0.18434*height))
// path.addCurve(to: CGPoint(x: 1.55391*width, y: -0.43648*height), control1: CGPoint(x: 1.58533*width, y: -0.37186*height), control2: CGPoint(x: 1.57251*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.50555*width, y: -0.23095*height), control1: CGPoint(x: 1.53324*width, y: -0.43648*height), control2: CGPoint(x: 1.51051*width, y: -0.35702*height))
// path.addCurve(to: CGPoint(x: 1.54647*width, y: 0.01271*height), control1: CGPoint(x: 1.49997*width, y: -0.09323*height), control2: CGPoint(x: 1.5194*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.60207*width, y: -0.19917*height), control1: CGPoint(x: 1.57231*width, y: 0.01271*height), control2: CGPoint(x: 1.5938*width, y: -0.08263*height))
// path.addCurve(to: CGPoint(x: 1.59979*width, y: -0.22248*height), control1: CGPoint(x: 1.60289*width, y: -0.21082*height), control2: CGPoint(x: 1.60227*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.59173*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.58615*width, y: -0.214*height), control1: CGPoint(x: 1.58925*width, y: -0.23943*height), control2: CGPoint(x: 1.58822*width, y: -0.23519*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.5318*width, y: -0.20659*height))
// path.addCurve(to: CGPoint(x: 1.532*width, y: -0.214*height), control1: CGPoint(x: 1.5318*width, y: -0.20871*height), control2: CGPoint(x: 1.532*width, y: -0.21188*height))
// path.addCurve(to: CGPoint(x: 1.55329*width, y: -0.32736*height), control1: CGPoint(x: 1.53428*width, y: -0.27863*height), control2: CGPoint(x: 1.54544*width, y: -0.32736*height))
// path.addCurve(to: CGPoint(x: 1.5597*width, y: -0.2977*height), control1: CGPoint(x: 1.55742*width, y: -0.32736*height), control2: CGPoint(x: 1.5597*width, y: -0.31465*height))
// path.addCurve(to: CGPoint(x: 1.5318*width, y: -0.20659*height), control1: CGPoint(x: 1.5597*width, y: -0.25426*height), control2: CGPoint(x: 1.54585*width, y: -0.20976*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.61481*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.64643*width, y: -0.0678*height), control1: CGPoint(x: 1.63011*width, y: 0.01271*height), control2: CGPoint(x: 1.64189*width, y: -0.0339*height))
// path.addCurve(to: CGPoint(x: 1.66958*width, y: 0.01271*height), control1: CGPoint(x: 1.64953*width, y: -0.01907*height), control2: CGPoint(x: 1.6578*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.7105*width, y: -0.19917*height), control1: CGPoint(x: 1.69273*width, y: 0.01271*height), control2: CGPoint(x: 1.70596*width, y: -0.11018*height))
// path.addCurve(to: CGPoint(x: 1.70823*width, y: -0.22248*height), control1: CGPoint(x: 1.71112*width, y: -0.21082*height), control2: CGPoint(x: 1.71071*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.70017*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.69542*width, y: -0.22248*height), control1: CGPoint(x: 1.69769*width, y: -0.23943*height), control2: CGPoint(x: 1.69624*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.67599*width, y: -0.09429*height), control1: CGPoint(x: 1.69252*width, y: -0.18328*height), control2: CGPoint(x: 1.68549*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.67123*width, y: -0.18434*height), control1: CGPoint(x: 1.6702*width, y: -0.09429*height), control2: CGPoint(x: 1.66958*width, y: -0.12819*height))
// path.addLine(to: CGPoint(x: 1.67785*width, y: -0.40258*height))
// path.addCurve(to: CGPoint(x: 1.67433*width, y: -0.42377*height), control1: CGPoint(x: 1.67826*width, y: -0.41423*height), control2: CGPoint(x: 1.67681*width, y: -0.42377*height))
// path.addLine(to: CGPoint(x: 1.6578*width, y: -0.42377*height))
// path.addCurve(to: CGPoint(x: 1.65305*width, y: -0.40258*height), control1: CGPoint(x: 1.65532*width, y: -0.42377*height), control2: CGPoint(x: 1.65346*width, y: -0.41529*height))
// path.addLine(to: CGPoint(x: 1.65284*width, y: -0.39622*height))
// path.addCurve(to: CGPoint(x: 1.63176*width, y: -0.43648*height), control1: CGPoint(x: 1.64953*width, y: -0.41105*height), control2: CGPoint(x: 1.64292*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.5867*width, y: -0.24473*height), control1: CGPoint(x: 1.61171*width, y: -0.43648*height), control2: CGPoint(x: 1.59332*width, y: -0.35596*height))
// path.addCurve(to: CGPoint(x: 1.61481*width, y: 0.01271*height), control1: CGPoint(x: 1.57926*width, y: -0.11865*height), control2: CGPoint(x: 1.58918*width, y: 0.01271*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.64974*width, y: -0.29346*height))
// path.addCurve(to: CGPoint(x: 1.62308*width, y: -0.09429*height), control1: CGPoint(x: 1.64685*width, y: -0.18858*height), control2: CGPoint(x: 1.63589*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.61212*width, y: -0.21294*height), control1: CGPoint(x: 1.61336*width, y: -0.09429*height), control2: CGPoint(x: 1.60902*width, y: -0.15044*height))
// path.addCurve(to: CGPoint(x: 1.63651*width, y: -0.3263*height), control1: CGPoint(x: 1.61585*width, y: -0.28604*height), control2: CGPoint(x: 1.62639*width, y: -0.3263*height))
// path.addCurve(to: CGPoint(x: 1.64974*width, y: -0.29346*height), control1: CGPoint(x: 1.64375*width, y: -0.3263*height), control2: CGPoint(x: 1.64767*width, y: -0.30511*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.68931*width, y: -0.02119*height))
// path.addCurve(to: CGPoint(x: 1.69282*width, y: 0), control1: CGPoint(x: 1.6891*width, y: -0.00953*height), control2: CGPoint(x: 1.69055*width, y: 0))
// path.addLine(to: CGPoint(x: 1.70935*width, y: 0))
// path.addCurve(to: CGPoint(x: 1.71411*width, y: -0.02119*height), control1: CGPoint(x: 1.71225*width, y: 0), control2: CGPoint(x: 1.71369*width, y: -0.00848*height))
// path.addLine(to: CGPoint(x: 1.71597*width, y: -0.08263*height))
// path.addCurve(to: CGPoint(x: 1.74366*width, y: -0.31677*height), control1: CGPoint(x: 1.71886*width, y: -0.17798*height), control2: CGPoint(x: 1.73292*width, y: -0.31677*height))
// path.addCurve(to: CGPoint(x: 1.748*width, y: -0.2532*height), control1: CGPoint(x: 1.74821*width, y: -0.31677*height), control2: CGPoint(x: 1.74924*width, y: -0.29346*height))
// path.addLine(to: CGPoint(x: 1.7447*width, y: -0.14832*height))
// path.addCurve(to: CGPoint(x: 1.76888*width, y: 0.01271*height), control1: CGPoint(x: 1.74201*width, y: -0.05933*height), control2: CGPoint(x: 1.7511*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.8098*width, y: -0.19917*height), control1: CGPoint(x: 1.79202*width, y: 0.01271*height), control2: CGPoint(x: 1.80525*width, y: -0.11018*height))
// path.addCurve(to: CGPoint(x: 1.80753*width, y: -0.22248*height), control1: CGPoint(x: 1.81042*width, y: -0.21082*height), control2: CGPoint(x: 1.81001*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.79946*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.79471*width, y: -0.22248*height), control1: CGPoint(x: 1.79698*width, y: -0.23943*height), control2: CGPoint(x: 1.79574*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.77528*width, y: -0.09429*height), control1: CGPoint(x: 1.79161*width, y: -0.18328*height), control2: CGPoint(x: 1.78479*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.77053*width, y: -0.18434*height), control1: CGPoint(x: 1.7695*width, y: -0.09429*height), control2: CGPoint(x: 1.76888*width, y: -0.12819*height))
// path.addLine(to: CGPoint(x: 1.7728*width, y: -0.26274*height))
// path.addCurve(to: CGPoint(x: 1.75358*width, y: -0.43648*height), control1: CGPoint(x: 1.7759*width, y: -0.35914*height), control2: CGPoint(x: 1.77032*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.72403*width, y: -0.34749*height), control1: CGPoint(x: 1.74056*width, y: -0.43648*height), control2: CGPoint(x: 1.73044*width, y: -0.39092*height))
// path.addLine(to: CGPoint(x: 1.72568*width, y: -0.40258*height))
// path.addCurve(to: CGPoint(x: 1.72217*width, y: -0.42377*height), control1: CGPoint(x: 1.7261*width, y: -0.41423*height), control2: CGPoint(x: 1.72465*width, y: -0.42377*height))
// path.addLine(to: CGPoint(x: 1.70584*width, y: -0.42377*height))
// path.addCurve(to: CGPoint(x: 1.70109*width, y: -0.40258*height), control1: CGPoint(x: 1.70336*width, y: -0.42377*height), control2: CGPoint(x: 1.7015*width, y: -0.41529*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.87397*width, y: -0.214*height))
// path.addCurve(to: CGPoint(x: 1.83883*width, y: -0.09641*height), control1: CGPoint(x: 1.86611*width, y: -0.13137*height), control2: CGPoint(x: 1.85144*width, y: -0.09641*height))
// path.addCurve(to: CGPoint(x: 1.82147*width, y: -0.14302*height), control1: CGPoint(x: 1.82994*width, y: -0.09641*height), control2: CGPoint(x: 1.82436*width, y: -0.11442*height))
// path.addCurve(to: CGPoint(x: 1.86901*width, y: -0.28604*height), control1: CGPoint(x: 1.83945*width, y: -0.1409*height), control2: CGPoint(x: 1.86425*width, y: -0.18434*height))
// path.addCurve(to: CGPoint(x: 1.84172*width, y: -0.43648*height), control1: CGPoint(x: 1.87314*width, y: -0.37186*height), control2: CGPoint(x: 1.86032*width, y: -0.43648*height))
// path.addCurve(to: CGPoint(x: 1.79336*width, y: -0.23095*height), control1: CGPoint(x: 1.82105*width, y: -0.43648*height), control2: CGPoint(x: 1.79832*width, y: -0.35702*height))
// path.addCurve(to: CGPoint(x: 1.83428*width, y: 0.01271*height), control1: CGPoint(x: 1.78778*width, y: -0.09323*height), control2: CGPoint(x: 1.80721*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.88988*width, y: -0.19917*height), control1: CGPoint(x: 1.86012*width, y: 0.01271*height), control2: CGPoint(x: 1.88161*width, y: -0.08263*height))
// path.addCurve(to: CGPoint(x: 1.8876*width, y: -0.22248*height), control1: CGPoint(x: 1.8907*width, y: -0.21082*height), control2: CGPoint(x: 1.89008*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.87954*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.87396*width, y: -0.214*height), control1: CGPoint(x: 1.87706*width, y: -0.23943*height), control2: CGPoint(x: 1.87603*width, y: -0.23519*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.81961*width, y: -0.20659*height))
// path.addCurve(to: CGPoint(x: 1.81982*width, y: -0.214*height), control1: CGPoint(x: 1.81961*width, y: -0.20871*height), control2: CGPoint(x: 1.81982*width, y: -0.21188*height))
// path.addCurve(to: CGPoint(x: 1.8411*width, y: -0.32736*height), control1: CGPoint(x: 1.82209*width, y: -0.27863*height), control2: CGPoint(x: 1.83325*width, y: -0.32736*height))
// path.addCurve(to: CGPoint(x: 1.84751*width, y: -0.2977*height), control1: CGPoint(x: 1.84524*width, y: -0.32736*height), control2: CGPoint(x: 1.84751*width, y: -0.31465*height))
// path.addCurve(to: CGPoint(x: 1.81961*width, y: -0.20659*height), control1: CGPoint(x: 1.84751*width, y: -0.25426*height), control2: CGPoint(x: 1.83366*width, y: -0.20976*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 1.95305*width, y: -0.22248*height))
// path.addCurve(to: CGPoint(x: 1.93177*width, y: -0.09429*height), control1: CGPoint(x: 1.94995*width, y: -0.17904*height), control2: CGPoint(x: 1.94169*width, y: -0.09429*height))
// path.addCurve(to: CGPoint(x: 1.92598*width, y: -0.16527*height), control1: CGPoint(x: 1.92701*width, y: -0.09429*height), control2: CGPoint(x: 1.92371*width, y: -0.11442*height))
// path.addCurve(to: CGPoint(x: 1.93342*width, y: -0.31253*height), control1: CGPoint(x: 1.92867*width, y: -0.22777*height), control2: CGPoint(x: 1.93301*width, y: -0.30405*height))
// path.addCurve(to: CGPoint(x: 1.92763*width, y: -0.38245*height), control1: CGPoint(x: 1.93487*width, y: -0.34749*height), control2: CGPoint(x: 1.93445*width, y: -0.36656*height))
// path.addCurve(to: CGPoint(x: 1.8987*width, y: -0.44813*height), control1: CGPoint(x: 1.9173*width, y: -0.40576*height), control2: CGPoint(x: 1.90469*width, y: -0.42589*height))
// path.addCurve(to: CGPoint(x: 1.88692*width, y: -0.52017*height), control1: CGPoint(x: 1.89911*width, y: -0.48945*height), control2: CGPoint(x: 1.89498*width, y: -0.52441*height))
// path.addCurve(to: CGPoint(x: 1.87865*width, y: -0.40258*height), control1: CGPoint(x: 1.87762*width, y: -0.51594*height), control2: CGPoint(x: 1.87369*width, y: -0.46508*height))
// path.addCurve(to: CGPoint(x: 1.87431*width, y: -0.21506*height), control1: CGPoint(x: 1.87989*width, y: -0.3655*height), control2: CGPoint(x: 1.87906*width, y: -0.27969*height))
// path.addCurve(to: CGPoint(x: 1.87576*width, y: -0.19493*height), control1: CGPoint(x: 1.87348*width, y: -0.20553*height), control2: CGPoint(x: 1.8739*width, y: -0.19811*height))
// path.addLine(to: CGPoint(x: 1.88547*width, y: -0.17586*height))
// path.addCurve(to: CGPoint(x: 1.88919*width, y: -0.18434*height), control1: CGPoint(x: 1.88733*width, y: -0.17268*height), control2: CGPoint(x: 1.88857*width, y: -0.17374*height))
// path.addCurve(to: CGPoint(x: 1.8958*width, y: -0.35279*height), control1: CGPoint(x: 1.89229*width, y: -0.23519*height), control2: CGPoint(x: 1.89456*width, y: -0.29346*height))
// path.addLine(to: CGPoint(x: 1.90386*width, y: -0.33372*height))
// path.addCurve(to: CGPoint(x: 1.90717*width, y: -0.29664*height), control1: CGPoint(x: 1.90717*width, y: -0.3263*height), control2: CGPoint(x: 1.9082*width, y: -0.31783*height))
// path.addCurve(to: CGPoint(x: 1.89952*width, y: -0.14408*height), control1: CGPoint(x: 1.90448*width, y: -0.23625*height), control2: CGPoint(x: 1.90118*width, y: -0.19387*height))
// path.addCurve(to: CGPoint(x: 1.9266*width, y: 0.01271*height), control1: CGPoint(x: 1.89704*width, y: -0.06145*height), control2: CGPoint(x: 1.90924*width, y: 0.01271*height))
// path.addCurve(to: CGPoint(x: 1.96814*width, y: -0.19917*height), control1: CGPoint(x: 1.94913*width, y: 0.01271*height), control2: CGPoint(x: 1.96359*width, y: -0.11018*height))
// path.addCurve(to: CGPoint(x: 1.96587*width, y: -0.22248*height), control1: CGPoint(x: 1.96876*width, y: -0.21082*height), control2: CGPoint(x: 1.96835*width, y: -0.21824*height))
// path.addLine(to: CGPoint(x: 1.95781*width, y: -0.23519*height))
// path.addCurve(to: CGPoint(x: 1.95305*width, y: -0.22248*height), control1: CGPoint(x: 1.95533*width, y: -0.23943*height), control2: CGPoint(x: 1.95388*width, y: -0.23519*height))
// path.closeSubpath()
// return path
// }
//}
//
//// Ghost
//struct Ghost: Shape {
// func path(in rect: CGRect) -> Path {
// var path = Path()
// let width = rect.size.width
// let height = rect.size.height
// path.move(to: CGPoint(x: 0.47*width, y: 0.0815*height))
// path.addCurve(to: CGPoint(x: 0.3148*width, y: 0.1708*height), control1: CGPoint(x: 0.4054*width, y: 0.0893*height), control2: CGPoint(x: 0.353*width, y: 0.1195*height))
// path.addCurve(to: CGPoint(x: 0.2539*width, y: 0.3318*height), control1: CGPoint(x: 0.2841*width, y: 0.2119*height), control2: CGPoint(x: 0.2634*width, y: 0.2668*height))
// path.addCurve(to: CGPoint(x: 0.2517*width, y: 0.4035*height), control1: CGPoint(x: 0.2515*width, y: 0.3483*height), control2: CGPoint(x: 0.2504*width, y: 0.3846*height))
// path.addCurve(to: CGPoint(x: 0.2525*width, y: 0.4167*height), control1: CGPoint(x: 0.2522*width, y: 0.4106*height), control2: CGPoint(x: 0.2526*width, y: 0.4166*height))
// path.addCurve(to: CGPoint(x: 0.2447*width, y: 0.4189*height), control1: CGPoint(x: 0.2524*width, y: 0.4169*height), control2: CGPoint(x: 0.2489*width, y: 0.4179*height))
// path.addCurve(to: CGPoint(x: 0.1841*width, y: 0.4547*height), control1: CGPoint(x: 0.2245*width, y: 0.4241*height), control2: CGPoint(x: 0.2051*width, y: 0.4355*height))
// path.addCurve(to: CGPoint(x: 0.15*width, y: 0.513*height), control1: CGPoint(x: 0.1646*width, y: 0.4723*height), control2: CGPoint(x: 0.15*width, y: 0.4974*height))
// path.addCurve(to: CGPoint(x: 0.1569*width, y: 0.5309*height), control1: CGPoint(x: 0.15*width, y: 0.5195*height), control2: CGPoint(x: 0.1528*width, y: 0.5268*height))
// path.addCurve(to: CGPoint(x: 0.2085*width, y: 0.5309*height), control1: CGPoint(x: 0.165*width, y: 0.539*height), control2: CGPoint(x: 0.1775*width, y: 0.539*height))
// path.addCurve(to: CGPoint(x: 0.264*width, y: 0.5252*height), control1: CGPoint(x: 0.2538*width, y: 0.5191*height), control2: CGPoint(x: 0.2539*width, y: 0.5191*height))
// path.addCurve(to: CGPoint(x: 0.272*width, y: 0.5335*height), control1: CGPoint(x: 0.2701*width, y: 0.529*height), control2: CGPoint(x: 0.2711*width, y: 0.53*height))
// path.addCurve(to: CGPoint(x: 0.4178*width, y: 0.7853*height), control1: CGPoint(x: 0.3036*width, y: 0.6544*height), control2: CGPoint(x: 0.355*width, y: 0.7433*height))
// path.addCurve(to: CGPoint(x: 0.5902*width, y: 0.7895*height), control1: CGPoint(x: 0.4685*width, y: 0.8193*height), control2: CGPoint(x: 0.5271*width, y: 0.8207*height))
// path.addCurve(to: CGPoint(x: 0.6492*width, y: 0.7534*height), control1: CGPoint(x: 0.6081*width, y: 0.7807*height), control2: CGPoint(x: 0.6235*width, y: 0.7713*height))
// path.addCurve(to: CGPoint(x: 0.7453*width, y: 0.709*height), control1: CGPoint(x: 0.7026*width, y: 0.7165*height), control2: CGPoint(x: 0.7188*width, y: 0.709*height))
// path.addCurve(to: CGPoint(x: 0.8012*width, y: 0.7341*height), control1: CGPoint(x: 0.7664*width, y: 0.709*height), control2: CGPoint(x: 0.7842*width, y: 0.717*height))
// path.addCurve(to: CGPoint(x: 0.816*width, y: 0.744*height), control1: CGPoint(x: 0.81*width, y: 0.7429*height), control2: CGPoint(x: 0.8116*width, y: 0.744*height))
// path.addCurve(to: CGPoint(x: 0.8269*width, y: 0.7335*height), control1: CGPoint(x: 0.8201*width, y: 0.744*height), control2: CGPoint(x: 0.8254*width, y: 0.7389*height))
// path.addCurve(to: CGPoint(x: 0.8265*width, y: 0.707*height), control1: CGPoint(x: 0.8284*width, y: 0.7278*height), control2: CGPoint(x: 0.8283*width, y: 0.7154*height))
// path.addCurve(to: CGPoint(x: 0.7937*width, y: 0.6544*height), control1: CGPoint(x: 0.8223*width, y: 0.687*height), control2: CGPoint(x: 0.8115*width, y: 0.6696*height))
// path.addCurve(to: CGPoint(x: 0.7576*width, y: 0.6304*height), control1: CGPoint(x: 0.7808*width, y: 0.6433*height), control2: CGPoint(x: 0.7711*width, y: 0.6369*height))
// path.addCurve(to: CGPoint(x: 0.6647*width, y: 0.6285*height), control1: CGPoint(x: 0.725*width, y: 0.6149*height), control2: CGPoint(x: 0.6977*width, y: 0.6143*height))
// path.addCurve(to: CGPoint(x: 0.6694*width, y: 0.6162*height), control1: CGPoint(x: 0.6633*width, y: 0.6291*height), control2: CGPoint(x: 0.6646*width, y: 0.6257*height))
// path.addCurve(to: CGPoint(x: 0.7549*width, y: 0.3918*height), control1: CGPoint(x: 0.7139*width, y: 0.5273*height), control2: CGPoint(x: 0.7443*width, y: 0.4478*height))
// path.addCurve(to: CGPoint(x: 0.759*width, y: 0.31*height), control1: CGPoint(x: 0.76*width, y: 0.3651*height), control2: CGPoint(x: 0.7616*width, y: 0.3337*height))
// path.addCurve(to: CGPoint(x: 0.6846*width, y: 0.1554*height), control1: CGPoint(x: 0.7528*width, y: 0.252*height), control2: CGPoint(x: 0.7264*width, y: 0.1972*height))
// path.addCurve(to: CGPoint(x: 0.5329*width, y: 0.0815*height), control1: CGPoint(x: 0.6431*width, y: 0.114*height), control2: CGPoint(x: 0.5913*width, y: 0.0888*height))
// path.addCurve(to: CGPoint(x: 0.47*width, y: 0.0815*height), control1: CGPoint(x: 0.518*width, y: 0.0797*height), control2: CGPoint(x: 0.4851*width, y: 0.0797*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.534*width, y: 0.0985*height))
// path.addCurve(to: CGPoint(x: 0.6525*width, y: 0.1488*height), control1: CGPoint(x: 0.577*width, y: 0.1042*height), control2: CGPoint(x: 0.6175*width, y: 0.1214*height))
// path.addCurve(to: CGPoint(x: 0.6948*width, y: 0.1922*height), control1: CGPoint(x: 0.663*width, y: 0.157*height), control2: CGPoint(x: 0.6865*width, y: 0.1812*height))
// path.addCurve(to: CGPoint(x: 0.741*width, y: 0.374*height), control1: CGPoint(x: 0.7344*width, y: 0.245*height), control2: CGPoint(x: 0.7506*width, y: 0.3087*height))
// path.addCurve(to: CGPoint(x: 0.6504*width, y: 0.6166*height), control1: CGPoint(x: 0.732*width, y: 0.435*height), control2: CGPoint(x: 0.7061*width, y: 0.5045*height))
// path.addCurve(to: CGPoint(x: 0.6311*width, y: 0.6609*height), control1: CGPoint(x: 0.6311*width, y: 0.6556*height), control2: CGPoint(x: 0.6301*width, y: 0.6579*height))
// path.addCurve(to: CGPoint(x: 0.6403*width, y: 0.6664*height), control1: CGPoint(x: 0.6324*width, y: 0.6646*height), control2: CGPoint(x: 0.6371*width, y: 0.6674*height))
// path.addCurve(to: CGPoint(x: 0.6505*width, y: 0.6588*height), control1: CGPoint(x: 0.6415*width, y: 0.666*height), control2: CGPoint(x: 0.6461*width, y: 0.6626*height))
// path.addCurve(to: CGPoint(x: 0.704*width, y: 0.6353*height), control1: CGPoint(x: 0.6679*width, y: 0.6436*height), control2: CGPoint(x: 0.685*width, y: 0.6362*height))
// path.addCurve(to: CGPoint(x: 0.7833*width, y: 0.6673*height), control1: CGPoint(x: 0.7305*width, y: 0.634*height), control2: CGPoint(x: 0.757*width, y: 0.6447*height))
// path.addCurve(to: CGPoint(x: 0.8108*width, y: 0.7143*height), control1: CGPoint(x: 0.7983*width, y: 0.6803*height), control2: CGPoint(x: 0.8081*width, y: 0.6969*height))
// path.addCurve(to: CGPoint(x: 0.8101*width, y: 0.719*height), control1: CGPoint(x: 0.8116*width, y: 0.7193*height), control2: CGPoint(x: 0.8115*width, y: 0.7198*height))
// path.addCurve(to: CGPoint(x: 0.8065*width, y: 0.716*height), control1: CGPoint(x: 0.8092*width, y: 0.7186*height), control2: CGPoint(x: 0.8076*width, y: 0.7172*height))
// path.addCurve(to: CGPoint(x: 0.7675*width, y: 0.6948*height), control1: CGPoint(x: 0.8*width, y: 0.7088*height), control2: CGPoint(x: 0.7812*width, y: 0.6986*height))
// path.addCurve(to: CGPoint(x: 0.7209*width, y: 0.6953*height), control1: CGPoint(x: 0.7554*width, y: 0.6916*height), control2: CGPoint(x: 0.7341*width, y: 0.6918*height))
// path.addCurve(to: CGPoint(x: 0.6629*width, y: 0.724*height), control1: CGPoint(x: 0.7013*width, y: 0.7005*height), control2: CGPoint(x: 0.6868*width, y: 0.7076*height))
// path.addCurve(to: CGPoint(x: 0.5548*width, y: 0.7864*height), control1: CGPoint(x: 0.5998*width, y: 0.7672*height), control2: CGPoint(x: 0.583*width, y: 0.7769*height))
// path.addCurve(to: CGPoint(x: 0.508*width, y: 0.7947*height), control1: CGPoint(x: 0.5376*width, y: 0.7921*height), control2: CGPoint(x: 0.5265*width, y: 0.7941*height))
// path.addCurve(to: CGPoint(x: 0.4626*width, y: 0.7888*height), control1: CGPoint(x: 0.489*width, y: 0.7953*height), control2: CGPoint(x: 0.4788*width, y: 0.794*height))
// path.addCurve(to: CGPoint(x: 0.3776*width, y: 0.725*height), control1: CGPoint(x: 0.4334*width, y: 0.7796*height), control2: CGPoint(x: 0.4033*width, y: 0.757*height))
// path.addCurve(to: CGPoint(x: 0.3451*width, y: 0.6763*height), control1: CGPoint(x: 0.3696*width, y: 0.715*height), control2: CGPoint(x: 0.3525*width, y: 0.6894*height))
// path.addCurve(to: CGPoint(x: 0.2886*width, y: 0.5293*height), control1: CGPoint(x: 0.3234*width, y: 0.638*height), control2: CGPoint(x: 0.3017*width, y: 0.5813*height))
// path.addCurve(to: CGPoint(x: 0.2683*width, y: 0.3864*height), control1: CGPoint(x: 0.2758*width, y: 0.4785*height), control2: CGPoint(x: 0.2692*width, y: 0.4321*height))
// path.addCurve(to: CGPoint(x: 0.2726*width, y: 0.3205*height), control1: CGPoint(x: 0.2677*width, y: 0.3559*height), control2: CGPoint(x: 0.2684*width, y: 0.3444*height))
// path.addCurve(to: CGPoint(x: 0.4818*width, y: 0.097*height), control1: CGPoint(x: 0.2961*width, y: 0.1862*height), control2: CGPoint(x: 0.3708*width, y: 0.1063*height))
// path.addCurve(to: CGPoint(x: 0.534*width, y: 0.0985*height), control1: CGPoint(x: 0.4951*width, y: 0.0959*height), control2: CGPoint(x: 0.5201*width, y: 0.0966*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.2544*width, y: 0.4372*height))
// path.addCurve(to: CGPoint(x: 0.2575*width, y: 0.4589*height), control1: CGPoint(x: 0.2547*width, y: 0.439*height), control2: CGPoint(x: 0.2561*width, y: 0.4488*height))
// path.addCurve(to: CGPoint(x: 0.2656*width, y: 0.5051*height), control1: CGPoint(x: 0.2597*width, y: 0.4749*height), control2: CGPoint(x: 0.2642*width, y: 0.5003*height))
// path.addCurve(to: CGPoint(x: 0.2618*width, y: 0.5053*height), control1: CGPoint(x: 0.266*width, y: 0.5067*height), control2: CGPoint(x: 0.2657*width, y: 0.5067*height))
// path.addCurve(to: CGPoint(x: 0.2233*width, y: 0.5101*height), control1: CGPoint(x: 0.2554*width, y: 0.503*height), control2: CGPoint(x: 0.246*width, y: 0.5042*height))
// path.addCurve(to: CGPoint(x: 0.1775*width, y: 0.5205*height), control1: CGPoint(x: 0.1947*width, y: 0.5175*height), control2: CGPoint(x: 0.1846*width, y: 0.5198*height))
// path.addCurve(to: CGPoint(x: 0.169*width, y: 0.519*height), control1: CGPoint(x: 0.1718*width, y: 0.5211*height), control2: CGPoint(x: 0.171*width, y: 0.521*height))
// path.addCurve(to: CGPoint(x: 0.1791*width, y: 0.4849*height), control1: CGPoint(x: 0.1641*width, y: 0.5141*height), control2: CGPoint(x: 0.1686*width, y: 0.499*height))
// path.addCurve(to: CGPoint(x: 0.2285*width, y: 0.4427*height), control1: CGPoint(x: 0.1894*width, y: 0.4709*height), control2: CGPoint(x: 0.2135*width, y: 0.4504*height))
// path.addCurve(to: CGPoint(x: 0.2527*width, y: 0.434*height), control1: CGPoint(x: 0.2342*width, y: 0.4398*height), control2: CGPoint(x: 0.2501*width, y: 0.4341*height))
// path.addCurve(to: CGPoint(x: 0.2544*width, y: 0.4372*height), control1: CGPoint(x: 0.2534*width, y: 0.434*height), control2: CGPoint(x: 0.2541*width, y: 0.4355*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.4813*width, y: 0.2149*height))
// path.addCurve(to: CGPoint(x: 0.4432*width, y: 0.2634*height), control1: CGPoint(x: 0.4651*width, y: 0.2208*height), control2: CGPoint(x: 0.4513*width, y: 0.2383*height))
// path.addCurve(to: CGPoint(x: 0.4356*width, y: 0.312*height), control1: CGPoint(x: 0.4376*width, y: 0.2805*height), control2: CGPoint(x: 0.4356*width, y: 0.2933*height))
// path.addCurve(to: CGPoint(x: 0.4404*width, y: 0.3387*height), control1: CGPoint(x: 0.4356*width, y: 0.3288*height), control2: CGPoint(x: 0.4366*width, y: 0.3342*height))
// path.addCurve(to: CGPoint(x: 0.4655*width, y: 0.3364*height), control1: CGPoint(x: 0.4439*width, y: 0.3427*height), control2: CGPoint(x: 0.4488*width, y: 0.3423*height))
// path.addCurve(to: CGPoint(x: 0.492*width, y: 0.3315*height), control1: CGPoint(x: 0.4793*width, y: 0.3316*height), control2: CGPoint(x: 0.4796*width, y: 0.3316*height))
// path.addCurve(to: CGPoint(x: 0.5195*width, y: 0.3324*height), control1: CGPoint(x: 0.4989*width, y: 0.3315*height), control2: CGPoint(x: 0.5113*width, y: 0.3319*height))
// path.addCurve(to: CGPoint(x: 0.5466*width, y: 0.3276*height), control1: CGPoint(x: 0.5369*width, y: 0.3334*height), control2: CGPoint(x: 0.5416*width, y: 0.3326*height))
// path.addCurve(to: CGPoint(x: 0.552*width, y: 0.2905*height), control1: CGPoint(x: 0.5526*width, y: 0.3216*height), control2: CGPoint(x: 0.5542*width, y: 0.3104*height))
// path.addCurve(to: CGPoint(x: 0.516*width, y: 0.22*height), control1: CGPoint(x: 0.5484*width, y: 0.2589*height), control2: CGPoint(x: 0.5346*width, y: 0.2319*height))
// path.addCurve(to: CGPoint(x: 0.4813*width, y: 0.2149*height), control1: CGPoint(x: 0.5055*width, y: 0.2133*height), control2: CGPoint(x: 0.4916*width, y: 0.2112*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.3452*width, y: 0.2165*height))
// path.addCurve(to: CGPoint(x: 0.306*width, y: 0.2848*height), control1: CGPoint(x: 0.3275*width, y: 0.2247*height), control2: CGPoint(x: 0.3104*width, y: 0.2544*height))
// path.addCurve(to: CGPoint(x: 0.3052*width, y: 0.3052*height), control1: CGPoint(x: 0.3053*width, y: 0.2892*height), control2: CGPoint(x: 0.305*width, y: 0.2984*height))
// path.addCurve(to: CGPoint(x: 0.3129*width, y: 0.3277*height), control1: CGPoint(x: 0.3056*width, y: 0.3193*height), control2: CGPoint(x: 0.3072*width, y: 0.3239*height))
// path.addCurve(to: CGPoint(x: 0.3367*width, y: 0.3279*height), control1: CGPoint(x: 0.3171*width, y: 0.3306*height), control2: CGPoint(x: 0.3204*width, y: 0.3306*height))
// path.addCurve(to: CGPoint(x: 0.3708*width, y: 0.3305*height), control1: CGPoint(x: 0.3529*width, y: 0.3252*height), control2: CGPoint(x: 0.3592*width, y: 0.3257*height))
// path.addCurve(to: CGPoint(x: 0.3942*width, y: 0.3371*height), control1: CGPoint(x: 0.3862*width, y: 0.3369*height), control2: CGPoint(x: 0.3912*width, y: 0.3383*height))
// path.addCurve(to: CGPoint(x: 0.3985*width, y: 0.3331*height), control1: CGPoint(x: 0.3957*width, y: 0.3366*height), control2: CGPoint(x: 0.3976*width, y: 0.3347*height))
// path.addCurve(to: CGPoint(x: 0.3964*width, y: 0.2647*height), control1: CGPoint(x: 0.4036*width, y: 0.3231*height), control2: CGPoint(x: 0.4026*width, y: 0.2895*height))
// path.addCurve(to: CGPoint(x: 0.3657*width, y: 0.2155*height), control1: CGPoint(x: 0.39*width, y: 0.2389*height), control2: CGPoint(x: 0.3788*width, y: 0.221*height))
// path.addCurve(to: CGPoint(x: 0.3452*width, y: 0.2165*height), control1: CGPoint(x: 0.3601*width, y: 0.2132*height), control2: CGPoint(x: 0.3514*width, y: 0.2136*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.4847*width, y: 0.4141*height))
// path.addCurve(to: CGPoint(x: 0.4028*width, y: 0.4568*height), control1: CGPoint(x: 0.4575*width, y: 0.417*height), control2: CGPoint(x: 0.4287*width, y: 0.432*height))
// path.addCurve(to: CGPoint(x: 0.372*width, y: 0.5039*height), control1: CGPoint(x: 0.3864*width, y: 0.4726*height), control2: CGPoint(x: 0.3753*width, y: 0.4895*height))
// path.addCurve(to: CGPoint(x: 0.3808*width, y: 0.5335*height), control1: CGPoint(x: 0.3692*width, y: 0.5161*height), control2: CGPoint(x: 0.3726*width, y: 0.5277*height))
// path.addCurve(to: CGPoint(x: 0.4297*width, y: 0.5298*height), control1: CGPoint(x: 0.389*width, y: 0.5392*height), control2: CGPoint(x: 0.4035*width, y: 0.5381*height))
// path.addCurve(to: CGPoint(x: 0.463*width, y: 0.521*height), control1: CGPoint(x: 0.4497*width, y: 0.5235*height), control2: CGPoint(x: 0.4592*width, y: 0.521*height))
// path.addCurve(to: CGPoint(x: 0.4835*width, y: 0.5305*height), control1: CGPoint(x: 0.4655*width, y: 0.521*height), control2: CGPoint(x: 0.4712*width, y: 0.5237*height))
// path.addLine(to: CGPoint(x: 0.5004*width, y: 0.5399*height))
// path.addLine(to: CGPoint(x: 0.5042*width, y: 0.5332*height))
// path.addCurve(to: CGPoint(x: 0.508*width, y: 0.5258*height), control1: CGPoint(x: 0.5063*width, y: 0.5295*height), control2: CGPoint(x: 0.508*width, y: 0.5262*height))
// path.addCurve(to: CGPoint(x: 0.4681*width, y: 0.5044*height), control1: CGPoint(x: 0.508*width, y: 0.5246*height), control2: CGPoint(x: 0.4716*width, y: 0.505*height))
// path.addCurve(to: CGPoint(x: 0.4329*width, y: 0.5114*height), control1: CGPoint(x: 0.4626*width, y: 0.5033*height), control2: CGPoint(x: 0.4517*width, y: 0.5055*height))
// path.addCurve(to: CGPoint(x: 0.3905*width, y: 0.5196*height), control1: CGPoint(x: 0.4037*width, y: 0.5206*height), control2: CGPoint(x: 0.3946*width, y: 0.5223*height))
// path.addCurve(to: CGPoint(x: 0.388*width, y: 0.5122*height), control1: CGPoint(x: 0.3883*width, y: 0.5182*height), control2: CGPoint(x: 0.388*width, y: 0.5172*height))
// path.addCurve(to: CGPoint(x: 0.4404*width, y: 0.448*height), control1: CGPoint(x: 0.3881*width, y: 0.4946*height), control2: CGPoint(x: 0.4088*width, y: 0.4693*height))
// path.addCurve(to: CGPoint(x: 0.4875*width, y: 0.4309*height), control1: CGPoint(x: 0.4554*width, y: 0.438*height), control2: CGPoint(x: 0.4691*width, y: 0.433*height))
// path.addCurve(to: CGPoint(x: 0.5648*width, y: 0.4481*height), control1: CGPoint(x: 0.5106*width, y: 0.4283*height), control2: CGPoint(x: 0.5396*width, y: 0.4347*height))
// path.addCurve(to: CGPoint(x: 0.5766*width, y: 0.453*height), control1: CGPoint(x: 0.5699*width, y: 0.4508*height), control2: CGPoint(x: 0.5752*width, y: 0.453*height))
// path.addCurve(to: CGPoint(x: 0.584*width, y: 0.445*height), control1: CGPoint(x: 0.5801*width, y: 0.453*height), control2: CGPoint(x: 0.584*width, y: 0.4488*height))
// path.addCurve(to: CGPoint(x: 0.5658*width, y: 0.43*height), control1: CGPoint(x: 0.584*width, y: 0.4402*height), control2: CGPoint(x: 0.5797*width, y: 0.4367*height))
// path.addCurve(to: CGPoint(x: 0.5108*width, y: 0.414*height), control1: CGPoint(x: 0.5463*width, y: 0.4206*height), control2: CGPoint(x: 0.5309*width, y: 0.4161*height))
// path.addCurve(to: CGPoint(x: 0.4847*width, y: 0.4141*height), control1: CGPoint(x: 0.5003*width, y: 0.4129*height), control2: CGPoint(x: 0.4958*width, y: 0.4129*height))
// path.closeSubpath()
// path.move(to: CGPoint(x: 0.4665*width, y: 0.893*height))
// path.addCurve(to: CGPoint(x: 0.3071*width, y: 0.9064*height), control1: CGPoint(x: 0.3762*width, y: 0.8947*height), control2: CGPoint(x: 0.3136*width, y: 0.8999*height))
// path.addCurve(to: CGPoint(x: 0.3093*width, y: 0.9095*height), control1: CGPoint(x: 0.3058*width, y: 0.9078*height), control2: CGPoint(x: 0.306*width, y: 0.9081*height))
// path.addCurve(to: CGPoint(x: 0.6915*width, y: 0.9164*height), control1: CGPoint(x: 0.3396*width, y: 0.9221*height), control2: CGPoint(x: 0.5731*width, y: 0.9264*height))
// path.addCurve(to: CGPoint(x: 0.7394*width, y: 0.9075*height), control1: CGPoint(x: 0.7204*width, y: 0.914*height), control2: CGPoint(x: 0.7389*width, y: 0.9105*height))
// path.addCurve(to: CGPoint(x: 0.4665*width, y: 0.893*height), control1: CGPoint(x: 0.7408*width, y: 0.8978*height), control2: CGPoint(x: 0.604*width, y: 0.8905*height))
// path.closeSubpath()
// return path
// }
//}
//
//// Aurora
//struct AuroraView: View {
//
// private enum AnimationProperties {
// static let animationSpeed: Double = 4
// static let timerDuration: TimeInterval = 3
// static let blurRadius: CGFloat = 130
// }
//
// @State private var timer = Timer.publish(every: AnimationProperties.timerDuration, on: .main, in: .common).autoconnect()
// @ObservedObject private var animator = CircleAnimator(colors: AuroraColors.all)
//
// var body: some View {
// ZStack {
// ZStack {
// ForEach(animator.circles) { circle in
// MovingCircle(originOffset: circle.position)
// .foregroundColor(circle.color)
// }
// }.blur(radius: AnimationProperties.blurRadius)
// }
// .onDisappear {
// timer.upstream.connect().cancel()
// }
// .onAppear {
// animateCircles()
// timer = Timer.publish(every: AnimationProperties.timerDuration, on: .main, in: .common).autoconnect()
// }
// .onReceive(timer) { _ in
// animateCircles()
// }
// }
//
// private func animateCircles() {
// withAnimation(.easeInOut(duration: AnimationProperties.animationSpeed)) {
// animator.animate()
// }
// }
//
//}
//
//private enum AuroraColors {
// static var all: [Color] {
// [
// // Color(red: 0/255, green: 0/255, blue: 128/255), // Dark Blue
// Color.purple, // Purple
// Color.pink, // Pink
// Color.orange, // Orange
// ]
// }
//}
//
//private struct MovingCircle: Shape {
//
// var originOffset: CGPoint
//
// var animatableData: CGPoint.AnimatableData {
// get {
// originOffset.animatableData
// }
// set {
// originOffset.animatableData = newValue
// }
// }
//
// func path(in rect: CGRect) -> Path {
// var path = Path()
//
// let adjustedX = rect.width * originOffset.x
// let adjustedY = rect.height * originOffset.y
// let smallestDimension = min(rect.width, rect.height)
// path.addArc(center: CGPoint(x: adjustedX, y: adjustedY), radius: smallestDimension/2, startAngle: .zero, endAngle: .degrees(360), clockwise: true)
// return path
// }
//}
//
//private class CircleAnimator: ObservableObject {
// class Circle: Identifiable {
// internal init(position: CGPoint, color: Color) {
// self.position = position
// self.color = color
// }
// var position: CGPoint
// let id = UUID().uuidString
// let color: Color
// }
//
// @Published private(set) var circles: [Circle] = []
//
//
// init(colors: [Color]) {
// circles = colors.map({ color in
// Circle(position: CircleAnimator.generateRandomPosition(), color: color)
// })
// }
//
// func animate() {
// objectWillChange.send()
// for circle in circles {
// circle.position = CircleAnimator.generateRandomPosition()
// }
// }
//
// static func generateRandomPosition() -> CGPoint {
// CGPoint(x: CGFloat.random(in: 0 ... 1), y: CGFloat.random(in: 0 ... 1))
// }
//}
//
//// Ghost View
//// ZStack {
//// LinearGradient(colors: [.pink, .orange], startPoint: .topLeading, endPoint: .bottomTrailing)
//// .edgesIgnoringSafeArea(.all)
//// .hueRotation(.degrees(animateGradient ? 45 : 0))
//// .onAppear {
//// withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) {
//// animateGradient.toggle()
//// }
//// }
//// .mask(
//// Ghost()
//// .frame(width: 200, height: 200)
//// )
//// }
//
//// Gradient Background
////ZStack {
//// LinearGradient(colors: [.pink, .orange], startPoint: .topLeading, endPoint: .bottomTrailing)
//// .edgesIgnoringSafeArea(.all)
//// .hueRotation(.degrees(animateGradient ? 45 : 0))
//// .onAppear {
//// withAnimation(.easeInOut(duration: 3).repeatForever(autoreverses: true)) {
//// animateGradient.toggle()
//// }
//// }
//// .mask(
//// Ghost()
//// .frame(width: 200, height: 200)
//// )
////}
+163 -149
View File
@@ -70,6 +70,46 @@ struct NavButtonBottomBarStyle: ButtonStyle {
} }
struct InfoButton: View {
@State private var isPopoverPresented: Bool = false
let text: String
let color: Color?
var body: some View {
Button(action: {
self.isPopoverPresented.toggle()
}) {
Image(systemName: "info.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 14, height: 14)
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
}
.buttonStyle(PlainButtonStyle())
.onHover { isHovered in
if isHovered {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
.popover(isPresented: $isPopoverPresented, arrowEdge: .bottom) {
VStack {
Spacer()
Text(text)
.font(.callout)
.frame(maxWidth: .infinity)
.padding()
Spacer()
}
.frame(width: 300)
}
.padding(.horizontal, 5)
}
}
struct WarningPopoverView: View { struct WarningPopoverView: View {
var label: String var label: String
var bodyText: String var bodyText: String
@@ -82,10 +122,14 @@ struct WarningPopoverView: View {
.foregroundStyle(.red) .foregroundStyle(.red)
.popover(isPresented: $isPresented, arrowEdge: .top) { .popover(isPresented: $isPresented, arrowEdge: .top) {
VStack { VStack {
Spacer()
Text(bodyText) Text(bodyText)
.font(.callout)
.frame(maxWidth: .infinity)
.padding() .padding()
.font(.title2) Spacer()
} }
.frame(width: 300)
} }
Text(label) Text(label)
@@ -100,6 +144,121 @@ struct WarningPopoverView: View {
} }
public struct NewFeatureView: View {
var text: String
var mini: Bool
@Binding var showFeature: Bool
public var body: some View {
VStack {
HStack {
Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!")
.font(.headline).bold()
Spacer()
Button("") {
withAnimation(Animation.easeInOut(duration: 0.5)) {
showFeature = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: .gray))
.onHover { isHovered in
if isHovered {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
}
.padding()
ScrollView() {
Text(text)
.font(.callout)
.multilineTextAlignment(.leading)
.padding(.trailing)
}
.padding(.horizontal)
.padding(.bottom)
}
.frame(maxWidth: mini ? 200 : 400, maxHeight: mini ? 300 : 250)
.background(.ultraThinMaterial)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.strokeBorder(Color("mode").opacity(0.2), lineWidth: 0.6)
)
.padding()
}
}
public struct SlideableDivider: View {
@Binding var dimension: Double
@State private var dimensionStart: Double?
@State private var handleWidth: Double = 2
@State private var handleHeight: Double = 30
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()
} else {
NSCursor.pop()
}
}
.contextMenu {
Button("Reset Size") {
dimension = 280
}
}
.gesture(drag)
Spacer()
}
.offset(x: 5)
)
}
var drag: some Gesture {
DragGesture(minimumDistance: 5, coordinateSpace: CoordinateSpace.global)
.onChanged { val in
if dimensionStart == nil {
dimensionStart = dimension
}
let delta = val.location.x - val.startLocation.x
let newDimension = dimensionStart! + Double(delta)
// Set minimum and maximum width
let minWidth: Double = 250
let maxWidth: Double = 350
dimension = max(minWidth, min(maxWidth, newDimension))
NSCursor.closedHand.set()
handleWidth = 4
handleHeight = 40
}
.onEnded { val in
dimensionStart = nil
NSCursor.arrow.set()
handleWidth = 2
handleHeight = 30
}
}
}
struct LabeledDivider: View { struct LabeledDivider: View {
let label: String let label: String
@@ -140,7 +299,6 @@ struct AnimatedSearchStyle: TextFieldStyle {
if isHovered || !text.isEmpty { if isHovered || !text.isEmpty {
configuration configuration
// .frame(minWidth: 130)
.frame(height: 15) .frame(height: 15)
.font(.title3) .font(.title3)
.textFieldStyle(PlainTextFieldStyle()) .textFieldStyle(PlainTextFieldStyle())
@@ -242,30 +400,9 @@ struct SimpleSearchStyle: TextFieldStyle {
} }
configuration configuration
// .frame(minWidth: mini ? 80 : 300)
// .frame(maxWidth: mini ? 150 : 300)
.font(.title3) .font(.title3)
.textFieldStyle(PlainTextFieldStyle()) .textFieldStyle(PlainTextFieldStyle())
// Image(systemName: "arrow.triangle.2.circlepath")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 15, height: 15)
// .padding(.trailing, 5)
// .foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
// .onTapGesture {
// withAnimation {
// // Refresh Apps list
// appState.reload.toggle()
// let sortedApps = getSortedApps()
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// appState.sortedApps.userApps = sortedApps.userApps
// appState.sortedApps.systemApps = sortedApps.systemApps
// appState.reload.toggle()
// }
// }
//
// }
if trash && text != "" { if trash && text != "" {
Image(systemName: "xmark") Image(systemName: "xmark")
@@ -472,42 +609,6 @@ private let redBG: AnyShapeStyle = AnyShapeStyle(
// Picker
//struct PillPicker: View {
// @Binding var index: Int
// var onTapAction: () -> Void
//
// var body: some View {
//
// HStack(alignment: .center, spacing: 20) {
// ForEach(0..<3) { i in
// HStack(spacing: 4) {
// Image(systemName: i == 0 ? "appclip" : "ellipsis.circle")
// .resizable()
// .scaledToFit()
// .frame(width: 16)
// .foregroundColor(index == i ? .black : .gray)
//
// Text(i == 0 ? "Apps" : (i == 1 ? "Widgets" : "Plugins"))
// .font(.system(size: 12))
// .foregroundColor(index == i ? .black : .gray)
// }
// .padding(8)
// .background((Color.white).opacity(index == i ? 1 : 0))
// .clipShape(Capsule())
// .onTapGesture {
// withAnimation {
// index = i
// onTapAction()
// }
// }
// }
// }
// .background(Color.black.opacity(0.06))
// .clipShape(Capsule())
// }
//}
struct PillPicker: View { struct PillPicker: View {
@Binding var index: Int @Binding var index: Int
var onTapAction: () -> Void var onTapAction: () -> Void
@@ -526,12 +627,6 @@ struct PillPicker: View {
.shadow(color: Color.black.opacity(0.2), radius: 2, x: 0, y: 2) .shadow(color: Color.black.opacity(0.2), radius: 2, x: 0, y: 2)
HStack(spacing: 0) { HStack(spacing: 0) {
// Uncomment below for icon next to labels
// Image(systemName: i == 0 ? "appclip" : (i == 1 ? "macwindow" : "cube.box.fill"))
// .resizable()
// .scaledToFit()
// .frame(width: 16)
// .foregroundColor(index == i ? .black : .gray)
Text(i == 0 ? "Apps" : (i == 1 ? "Widgets" : "Plugins")) Text(i == 0 ? "Apps" : (i == 1 ? "Widgets" : "Plugins"))
.font(.system(size: 12)) .font(.system(size: 12))
@@ -561,7 +656,6 @@ struct PillPicker: View {
.offset(y: 1) .offset(y: 1)
) )
) )
// .background(Color.black.opacity(0.06))
.clipShape(Capsule()) .clipShape(Capsule())
} }
} }
@@ -574,11 +668,11 @@ struct PearDropView: View {
var body: some View { var body: some View {
VStack(alignment: .center, spacing: 0) { VStack(alignment: .center, spacing: 0) {
HStack(spacing: 0) { HStack(spacing: 0) {
LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing) LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.frame(width: 195) .frame(width: 195)
LinearGradient(gradient: Gradient(colors: [.orange, .gray]), startPoint: .leading, endPoint: .trailing) LinearGradient(gradient: Gradient(colors: [.orange, Color("mode").opacity(0.5)]), startPoint: .leading, endPoint: .trailing)
.frame(width: 10) .frame(width: 10)
LinearGradient(gradient: Gradient(colors: [.gray, .gray]), startPoint: .leading, endPoint: .trailing) LinearGradient(gradient: Gradient(colors: [Color("mode").opacity(0.5), Color("mode").opacity(0.5)]), startPoint: .leading, endPoint: .trailing)
.frame(width: 300) .frame(width: 300)
} }
.mask( .mask(
@@ -588,90 +682,10 @@ struct PearDropView: View {
.frame(width: 500, height: 120) .frame(width: 500, height: 120)
.padding() .padding()
) )
// LinearGradient(gradient: Gradient(colors: [.gray, .gray]), startPoint: .leading, endPoint: .trailing)
// .mask(
// Image("logo_text_small")
// .resizable()
// .scaledToFit()
// .frame(minWidth: 300, maxWidth: 350)
// .frame(height: 100)
// .padding()
// )
// LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
// .mask(
// Image(systemName: "plus.square.dashed")
// .resizable()
// .scaledToFit()
// .frame(width: 50, height: 50)
// .padding()
// )
} }
.frame(height: 120) .frame(height: 120)
} }
} }
// .frame(height: 50)
// HStack(spacing: 10) {
// HStack(spacing: 2) {
// Image(systemName: "appclip")
// .foregroundColor(index == 0 ? .black : .gray)
// Text("Apps")
// .font(.footnote)
// .foregroundColor(index == 0 ? .black : .gray)
// }
// .padding(8)
// .background((Color.white).opacity(index == 0 ? 1 : 0))
// .clipShape(Capsule())
// .onTapGesture {
// withAnimation {
// index = 0
// onTapAction()
// }
// }
//
// HStack(spacing: 2) {
// Image(systemName: "ellipsis.circle")
// .foregroundColor(index == 1 ? .black : .gray)
// Text("Widgets")
// .foregroundColor(index == 1 ? .black : .gray)
//
//
// }
// .padding(8)
// .background((Color.white).opacity(index == 1 ? 1 : 0))
// .clipShape(Capsule())
// .onTapGesture {
// withAnimation {
// index = 1
// onTapAction()
// }
// }
//
// HStack(spacing: 2) {
// Image(systemName: "ellipsis.circle")
// .foregroundColor(index == 2 ? .black : .gray)
// Text("Plugins")
// .foregroundColor(index == 2 ? .black : .gray)
//
//
// }
// .padding(8)
// .background((Color.white).opacity(index == 2 ? 2 : 0))
// .clipShape(Capsule())
// .onTapGesture {
// withAnimation {
// index = 2
// onTapAction()
// }
// }
// }
// .background(Color.black.opacity(0.06))
// .clipShape(Capsule())
// .frame(height: 50)
+7 -80
View File
@@ -53,7 +53,6 @@ func checkFullDiskAccessForApp() -> Bool {
process.standardError = pipeErr process.standardError = pipeErr
process.launch() process.launch()
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile() let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile()
let output = String(data: dataErr, encoding: .utf8) let output = String(data: dataErr, encoding: .utf8)
@@ -86,7 +85,6 @@ func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false)
process.standardError = pipeErr process.standardError = pipeErr
process.launch() process.launch()
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile() let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile()
let output = String(data: dataErr, encoding: .utf8) let output = String(data: dataErr, encoding: .utf8)
@@ -100,32 +98,11 @@ func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false)
diskP = false diskP = false
if !skipAlert { if !skipAlert {
NewWin.show(appState: appState, width: 500, height: 350, newWin: .perm) NewWin.show(appState: appState, width: 500, height: 350, newWin: .perm)
// appState.alertType = .diskAccess
// appState.showAlert = true
} }
return false return false
} }
// let fileURL = URL(fileURLWithPath: "/Library/Application Support/com.apple.TCC/TCC.db")
//
// let accessStatus = FileManager.default.isReadableFile(atPath: fileURL.path)
// printOS(accessStatus)
// if accessStatus {
// diskP = true
// _ = checkAndRequestAccessibilityAccess(appState: appState)
//
// return true
// } else {
// diskP = false
// if !skipAlert {
// appState.alertType = .diskAccess
// appState.showAlert = true
// }
//
// return false
// }
} }
@@ -260,6 +237,13 @@ extension String {
} }
} }
// --- Extend string to replace - and | with custom characters
extension String {
func featureFormat() -> String {
return self.replacingOccurrences(of: "- ", with: "").replacingOccurrences(of: "|", with: "\n\n")
}
}
// --- Trash Relationship --- // --- Trash Relationship ---
extension FileManager { extension FileManager {
@@ -279,21 +263,6 @@ func printOS(_ items: Any..., separator: String = " ", terminator: String = "\n"
} }
// --- Gradient ---
func schemeGradient(for colorScheme: ColorScheme) -> LinearGradient {
return LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
// switch colorScheme {
// case .light:
// return LinearGradient(gradient: Gradient(colors: [Color("AccentColor")]), startPoint: .leading, endPoint: .trailing)
// case .dark:
// return LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
// @unknown default:
// return LinearGradient(gradient: Gradient(colors: [Color("AccentColor")]), startPoint: .leading, endPoint: .trailing)
// }
}
// Get total size of folders and files using DU cli command // Get total size of folders and files using DU cli command
func totalSizeOnDisk(for paths: [URL]) -> Int64 { func totalSizeOnDisk(for paths: [URL]) -> Int64 {
@@ -354,7 +323,6 @@ extension Array where Element == URL {
var totalSize = 0 var totalSize = 0
for path in self { for path in self {
// if path.absoluteString.contains(".app") {
let resourceValues = try path.resourceValues(forKeys: [.isDirectoryKey, .isPackageKey, .totalFileAllocatedSizeKey]) let resourceValues = try path.resourceValues(forKeys: [.isDirectoryKey, .isPackageKey, .totalFileAllocatedSizeKey])
if resourceValues.isDirectory == true || path.pathExtension == "app" { if resourceValues.isDirectory == true || path.pathExtension == "app" {
@@ -370,7 +338,6 @@ extension Array where Element == URL {
} else { } else {
totalSize += resourceValues.totalFileAllocatedSize ?? 0 totalSize += resourceValues.totalFileAllocatedSize ?? 0
} }
// }
} }
@@ -396,7 +363,6 @@ func presentAlert(appState: AppState) -> Alert {
case .update: case .update:
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Install")) { return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Install")) {
downloadUpdate(appState: appState) downloadUpdate(appState: appState)
// launchUpdate()
appState.alertType = .off appState.alertType = .off
}, secondaryButton: .cancel()) }, secondaryButton: .cancel())
case .no_update: case .no_update:
@@ -525,10 +491,6 @@ func launchctl(load: Bool) {
task.launch() task.launch()
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
// if let output = String(data: data, encoding: .utf8) {
// printOS("Output: \(output)")
// }
} }
} }
@@ -543,40 +505,5 @@ func getCurrentTimestamp() -> String {
} }
// Execute sudo command
//func executePrivilegedCommand(launchPath: String, arguments: [String]) {
// let task = Process()
// task.launchPath = launchPath
// task.arguments = arguments
//
// var authorization: AuthorizationRef? = nil
// let status = AuthorizationCreate(nil, nil, AuthorizationFlags(), &authorization)
//
// if status == errAuthorizationSuccess {
// if let rightName = (kAuthorizationRightExecute as NSString).utf8String {
// let item = AuthorizationItem(name: rightName, valueLength: 0, value: nil, flags: 0)
// var items = [item]
// items.withUnsafeMutableBufferPointer { bufferPointer in
// var rights = AuthorizationRights(count: UInt32(bufferPointer.count), items: bufferPointer.baseAddress)
//
// let status = AuthorizationCopyRights(authorization!, &rights, nil, AuthorizationFlags([.extendRights, .interactionAllowed]), nil)
//
// if status == errAuthorizationSuccess {
// task.launch()
// } else {
// printOS("Authorization failed.")
// }
// }
// } else {
// printOS("Failed to convert rightName to C-string.")
// }
// } else {
// printOS("Authorization creation failed.")
// }
//}
// executePrivilegedCommand(launchPath: "/bin/mv", arguments: ["-f"] + filesSudoPaths + ["\(home)/.Trash"])
+19 -5
View File
@@ -21,8 +21,11 @@ struct PearcleanerApp: App {
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.miniview") private var miniView: Bool = true @AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@AppStorage("settings.general.features") private var features: String = ""
@State private var search = "" @State private var search = ""
@State private var showPopover: Bool = false @State private var showPopover: Bool = false
@State private var showFeature: Bool = false
var body: some Scene { var body: some Scene {
@@ -30,7 +33,7 @@ struct PearcleanerApp: App {
WindowGroup { WindowGroup {
Group { Group {
ZStack() {
if !mini { if !mini {
AppListView(search: $search, showPopover: $showPopover) AppListView(search: $search, showPopover: $showPopover)
.environmentObject(locations) .environmentObject(locations)
@@ -39,6 +42,14 @@ struct PearcleanerApp: App {
.environmentObject(locations) .environmentObject(locations)
} }
if showFeature {
NewFeatureView(text: features, mini: mini, showFeature: $showFeature)
.transition(.opacity)
}
}
} }
.environmentObject(appState) .environmentObject(appState)
.preferredColorScheme(displayMode.colorScheme) .preferredColorScheme(displayMode.colorScheme)
@@ -86,18 +97,21 @@ struct PearcleanerApp: App {
appState.sortedApps.systemApps = sortedApps.systemApps appState.sortedApps.systemApps = sortedApps.systemApps
// Find all app paths on load
if instantSearch {
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
getFeatures(appState: appState, show: $showFeature, features: $features)
#if !DEBUG #if !DEBUG
Task { Task {
// Find all app paths on load
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// Make sure App Support folder exists in the future if needed for storage // Make sure App Support folder exists in the future if needed for storage
ensureApplicationSupportFolderExists(appState: appState) ensureApplicationSupportFolderExists(appState: appState)
// Check for updates 1 minute after app launch // Check for updates after app launch
if diskP { if diskP {
loadGithubReleases(appState: appState) loadGithubReleases(appState: appState)
} }
@@ -111,7 +125,7 @@ struct PearcleanerApp: App {
// TIMERS //////////////////////////////////////////////////////////////////////////////////// // TIMERS ////////////////////////////////////////////////////////////////////////////////////
// Check for app updates every 8 hours or whatever user saved setting. Also refresh autosuggestion list // Check for app updates every 8 hours or whatever user saved setting.
let updateSeconds = updateTimeframe.daysToSeconds let updateSeconds = updateTimeframe.daysToSeconds
_ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in _ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in
DispatchQueue.main.async { DispatchQueue.main.async {
@@ -7,7 +7,6 @@
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "icon_16x16@2x.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "16x16" "size" : "16x16"
@@ -19,7 +18,6 @@
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "icon_32x32@2x.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "32x32" "size" : "32x32"
@@ -31,7 +29,6 @@
"size" : "128x128" "size" : "128x128"
}, },
{ {
"filename" : "icon_128x128@2x.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "128x128" "size" : "128x128"
@@ -43,7 +40,6 @@
"size" : "256x256" "size" : "256x256"
}, },
{ {
"filename" : "icon_256x256@2x.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "256x256" "size" : "256x256"
@@ -55,7 +51,6 @@
"size" : "512x512" "size" : "512x512"
}, },
{ {
"filename" : "icon_512x512@2x.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "512x512" "size" : "512x512"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 KiB

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "logo_combo_small.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "pear_small.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

+2 -1
View File
@@ -36,10 +36,11 @@ struct AboutSettingsTab: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
HStack() { HStack() {
Text("Credits and Inspiration").font(.title2) Text("Credits").font(.title2)
Spacer() Spacer()
} }
.padding(.leading) .padding(.leading)
.padding(.bottom)
HStack{ HStack{
Image(systemName: "paintbrush.pointed") Image(systemName: "paintbrush.pointed")
+26 -15
View File
@@ -19,9 +19,10 @@ struct GeneralSettingsTab: View {
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280 @AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false @AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
@State private var diskStatus: Bool = false @State private var diskStatus: Bool = false
@State private var accessStatus: Bool = false @State private var accessStatus: Bool = false
@State private var selectedTheme = "Auto"
private let themes = ["Auto", "Dark", "Light"] private let themes = ["Auto", "Dark", "Light"]
@Binding var showPopover: Bool @Binding var showPopover: Bool
@@ -57,25 +58,24 @@ struct GeneralSettingsTab: View {
HStack(spacing: 0) { HStack(spacing: 0) {
Image(systemName: "sidebar.left") Image(systemName: instantSearch ? "bolt" : "bolt.slash")
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(.gray) .foregroundStyle(.gray)
Text("Adjust sidebar width") VStack(alignment: .leading, spacing: 5) {
Text("\(instantSearch ? "Instant search is enabled" : "Instant search is disabled")")
.font(.callout) .font(.callout)
.foregroundStyle(.gray) .foregroundStyle(.gray)
}
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)
Spacer() Spacer()
Slider(value: $sidebarWidth, in: 200...360) { Toggle(isOn: $instantSearch, label: {
// Text("\(Int(sidebarWidth))") })
} .toggleStyle(.switch)
.padding(.horizontal)
.frame(width: 185)
Button("") {
sidebarWidth = 280
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reset to default size", color: Color("mode")))
} }
.padding(5) .padding(5)
.padding(.leading) .padding(.leading)
@@ -89,7 +89,7 @@ struct GeneralSettingsTab: View {
.padding(.trailing) .padding(.trailing)
.foregroundStyle(.gray) .foregroundStyle(.gray)
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text("Set color mode") Text("Set application color mode")
.font(.callout) .font(.callout)
.foregroundStyle(.gray) .foregroundStyle(.gray)
} }
@@ -143,7 +143,7 @@ struct GeneralSettingsTab: View {
.padding(.trailing) .padding(.trailing)
.foregroundStyle(.gray) .foregroundStyle(.gray)
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text("\(mini ? "Mini window mode" : "Full size window mode")") Text("\(mini ? "Mini window mode selected" : "Full size window mode selected")")
.font(.callout) .font(.callout)
.foregroundStyle(.gray) .foregroundStyle(.gray)
} }
@@ -182,6 +182,9 @@ struct GeneralSettingsTab: View {
.font(.callout) .font(.callout)
.foregroundStyle(.gray) .foregroundStyle(.gray)
} }
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil)
Spacer() Spacer()
Toggle(isOn: $miniView, label: { Toggle(isOn: $miniView, label: {
}) })
@@ -202,10 +205,13 @@ struct GeneralSettingsTab: View {
.padding(.trailing) .padding(.trailing)
.foregroundStyle(.gray) .foregroundStyle(.gray)
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text("\(popoverStay ? "Popover window doesn't hide on outside click" : "Popover window hides on outside click")") Text("\(popoverStay ? "Popover window will stay on top" : "Popover window will not stay on top")")
.font(.callout) .font(.callout)
.foregroundStyle(.gray) .foregroundStyle(.gray)
} }
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)
Spacer() Spacer()
Toggle(isOn: $popoverStay, label: { Toggle(isOn: $popoverStay, label: {
}) })
@@ -330,6 +336,11 @@ struct GeneralSettingsTab: View {
.onAppear { .onAppear {
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true) diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
accessStatus = checkAndRequestAccessibilityAccess(appState: appState) accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
// if displayMode.colorScheme == .dark {
// selectedTheme = "Dark"
// } else if displayMode.colorScheme == .light {
// selectedTheme = "Light"
// }
} }
} }
-150
View File
@@ -1,150 +0,0 @@
//
// Permissions.swift
// Pearcleaner
//
// Created by Alin Lupascu on 11/5/23.
//
import Foundation
import SwiftUI
struct PermissionsSettingsTab: View {
@EnvironmentObject var appState: AppState
@State private var diskStatus: Bool = false
@State private var accessStatus: Bool = false
var body: some View {
Form {
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 0) {
Image(systemName: diskStatus ? "externaldrive" : "externaldrive")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(diskStatus ? .green : .red)
Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted")
.font(.callout)
.foregroundStyle(.gray)
Spacer()
Button("") {
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "gear", help: "View disk permissions pane", color: Color("mode")))
}
.padding(5)
HStack(spacing: 0) {
Image(systemName: accessStatus ? "accessibility" : "accessibility")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(accessStatus ? .green : .red)
Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted")
.font(.callout)
.foregroundStyle(.gray)
Spacer()
Button("") {
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "gear", help: "View accessibility permissions pane", color: Color("mode")))
}
.padding(5)
// HStack(spacing: 15) {
// Image(systemName: "internaldrive")
// .resizable()
// .scaledToFit()
// .frame(width: 20)
//
// VStack(alignment: .leading, spacing: 5) {
// Text("Full Disk")
// Text(diskStatus ? "Permission granted" : "Permission not granted")
// .font(.footnote)
// .foregroundStyle(diskStatus ? .green : .red)
// }
//
// Spacer()
//
// Button("") {
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
// NSWorkspace.shared.open(url)
// }
// }
// .buttonStyle(SimpleButtonStyle(icon: "paperplane", help: "View disk permission pane", color: Color("AccentColor")))
// }
// .padding()
// .background(
// RoundedRectangle(cornerRadius: 8)
// .fill(Color("mode").opacity(0.05))
// )
// HStack(spacing: 15) {
// Image(systemName: "accessibility")
// .resizable()
// .scaledToFit()
// .frame(width: 20)
//
// VStack(alignment: .leading, spacing: 5) {
// Text("Accessibility")
// Text(accessStatus ? "Permission granted" : "Permission not granted")
// .font(.footnote)
// .foregroundStyle(accessStatus ? .green : .red)
// }
//
// Spacer()
//
// Button("") {
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
// NSWorkspace.shared.open(url)
// }
// }
// .buttonStyle(SimpleButtonStyle(icon: "paperplane", help: "View accessibility pane", color: Color("AccentColor")))
// }
// .padding()
// .background(
// RoundedRectangle(cornerRadius: 8)
// .fill(Color("mode").opacity(0.05))
// )
// HStack(alignment: .center) {
// Spacer()
// Button("") {
// relaunchApp()
// }
// .buttonStyle(SimpleButtonStyle(icon: "restart", help: "Restart application", color: Color("AccentColor")))
// Text("Restart")
// Spacer()
// }
// .padding()
}
}
.padding(20)
.frame(width: 400, height: 200)
.onAppear {
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
}
}
}
-56
View File
@@ -1,56 +0,0 @@
//
// Sentinel.swift
// Pearcleaner
//
// Created by Alin Lupascu on 11/9/23.
//
import Foundation
import SwiftUI
struct SentinelSettingsTab: View {
@EnvironmentObject var appState: AppState
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
var body: some View {
Form {
VStack {
HStack {
VStack(alignment: .leading, spacing: 5) {
Text("Sentinel Monitor").font(.title2)
Text("Detects when apps are moved to Trash for file removal")
.font(.footnote)
.foregroundStyle(.gray)
}
Spacer()
Toggle(isOn: $sentinel, label: {
})
.toggleStyle(SentinelToggleStyle())
.onChange(of: sentinel) { newValue in
if newValue {
launchctl(load: true)
} else {
launchctl(load: false)
}
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color("mode").opacity(0.05))
)
Text("If you uninstall Pearcleaner, make sure to disable Sentinel first").font(.footnote)
.foregroundStyle(Color.red)
.padding()
}
}
.padding(20)
.frame(width: 450, height: 130)
}
}
+2 -12
View File
@@ -20,18 +20,6 @@ struct SettingsView: View {
} }
.tag(CurrentTabView.general) .tag(CurrentTabView.general)
// PermissionsSettingsTab()
// .tabItem {
// Label(CurrentTabView.permissions.title, systemImage: "lock")
// }
// .tag(CurrentTabView.permissions)
// SentinelSettingsTab()
// .tabItem {
// Label(CurrentTabView.sentinel.title, systemImage: "eye.circle")
// }
// .tag(CurrentTabView.sentinel)
UpdateSettingsTab() UpdateSettingsTab()
.tabItem { .tabItem {
Label(CurrentTabView.update.title, systemImage: "cloud") Label(CurrentTabView.update.title, systemImage: "cloud")
@@ -56,3 +44,5 @@ struct SettingsView: View {
+3 -19
View File
@@ -71,24 +71,26 @@ struct UpdateSettingsTab: View {
HStack(alignment: .center, spacing: 20) { HStack(alignment: .center, spacing: 20) {
Spacer() Spacer()
Button("Update Notes"){ Button("Update Notes"){
loadGithubReleases(appState: appState) loadGithubReleases(appState: appState)
} }
.buttonStyle(SimpleButtonStyle(icon: "list.bullet", help: "Check release notes", color: Color("mode"))) .buttonStyle(SimpleButtonStyle(icon: "list.bullet", help: "Check release notes", color: Color("mode")))
Spacer()
Button("Force Update"){ Button("Force Update"){
loadGithubReleases(appState: appState, manual: true) 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", color: Color("mode")))
Spacer()
Button("GitHub"){ Button("GitHub"){
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!) 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", color: Color("mode")))
Spacer() Spacer()
} }
.padding() .padding()
@@ -130,13 +132,9 @@ func checkForUpdate(appState: AppState, manual: Bool = false) {
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
if latestRelease.tag_name > currentVersion ?? "" { if latestRelease.tag_name > currentVersion ?? "" {
NewWin.show(appState: appState, width: 500, height: 440, newWin: .update) NewWin.show(appState: appState, width: 500, height: 440, newWin: .update)
// appState.alertType = .update
// appState.showAlert = true
} else { } else {
if manual { if manual {
NewWin.show(appState: appState, width: 500, height: 300, newWin: .no_update) NewWin.show(appState: appState, width: 500, height: 300, newWin: .no_update)
// appState.alertType = .no_update
// appState.showAlert = true
} }
} }
} }
@@ -215,22 +213,14 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
// Unzip the downloaded update file to your app's bundle path // Unzip the downloaded update file to your app's bundle path
let process = Process() let process = Process()
// process.executableURL = URL(fileURLWithPath: "/usr/bin/unzip")
// process.arguments = [fileURL, "-d", appDirectory]
process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto") process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
process.arguments = ["-xk", fileURL, appDirectory] process.arguments = ["-xk", fileURL, appDirectory]
// let pipe = Pipe()
process.standardOutput = FileHandle.nullDevice process.standardOutput = FileHandle.nullDevice
process.standardError = FileHandle.nullDevice process.standardError = FileHandle.nullDevice
try process.run() try process.run()
process.waitUntilExit() process.waitUntilExit()
// let outputData = pipe.fileHandleForReading.readDataToEndOfFile()
// let output = String(decoding: outputData, as: UTF8.self)
//
// writeLog(string: output)
updateOnMain { updateOnMain {
appState.progressBar.0 = "Deleting update file" appState.progressBar.0 = "Deleting update file"
appState.progressBar.1 = 0.8 appState.progressBar.1 = 0.8
@@ -239,12 +229,6 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
// After unzipping, remove the update file // After unzipping, remove the update file
try fileManager.removeItem(atPath: fileURL) try fileManager.removeItem(atPath: fileURL)
// Show Restart dialog
// updateOnMain {
// appState.alertType = .restartApp
// appState.showAlert = true
// }
} catch { } catch {
printOS("Error updating the app: \(error)") printOS("Error updating the app: \(error)")
+11 -32
View File
@@ -26,8 +26,18 @@ struct AppListItems: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .center) { HStack(alignment: .center) {
if isHovered || isSelected {
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 {
Image(nsImage: appIcon) Image(nsImage: appIcon)
.resizable() .resizable()
@@ -35,6 +45,7 @@ struct AppListItems: View {
.frame(width: 30, height: 30) .frame(width: 30, height: 30)
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
} }
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text(appInfo.appName) Text(appInfo.appName)
.font(.system(size: 12)) .font(.system(size: 12))
@@ -56,8 +67,6 @@ struct AppListItems: View {
.background( .background(
Capsule().strokeBorder(Color("mode").opacity(0.3), lineWidth: 1) Capsule().strokeBorder(Color("mode").opacity(0.3), lineWidth: 1)
) )
// .background(Color("mode").opacity(0.1))
// .clipShape(.capsule)
} }
if appInfo.wrapped { if appInfo.wrapped {
Text("iOS") Text("iOS")
@@ -68,22 +77,12 @@ struct AppListItems: View {
.background( .background(
Capsule().strokeBorder(Color("mode").opacity(0.3), lineWidth: 1) Capsule().strokeBorder(Color("mode").opacity(0.3), lineWidth: 1)
) )
// .background(Color("mode").opacity(0.1))
// .clipShape(.capsule)
} }
Text(appInfo.appVersion) Text(appInfo.appVersion)
.font(.footnote) .font(.footnote)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
if isHovered || isSelected {
RoundedRectangle(cornerRadius: 50)
.fill(isSelected ? Color("AccentColor") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 4 : 2, height: 30)
.padding(.leading, 5)
.offset(x: 20)
}
} }
} }
@@ -97,25 +96,5 @@ struct AppListItems: View {
.onTapGesture { .onTapGesture {
showAppInFiles(appInfo: appInfo, mini: mini, appState: appState, locations: locations, showPopover: $showPopover) showAppInFiles(appInfo: appInfo, mini: mini, appState: appState, locations: locations, showPopover: $showPopover)
} }
// .popover(isPresented: Binding(
// get: { showPopover && appState.appInfo.id == appInfo.id},
// set: { _ in showPopover = false}
// ), attachmentAnchor: .rect(.rect(CGRect(x: windowSettings.loadWindowSettings().width - 17, y: 15, width: 0, height: 0))), arrowEdge: .trailing) {
// .popover(isPresented: $showPopover, arrowEdge: .trailing) {
// VStack {
// FilesView(showPopover: $showPopover, search: $search)
// .id(appState.appInfo.id)
// }
// .interactiveDismissDisabled(popoverStay)
// .background(
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
// )
// .frame(minWidth: 650, minHeight: 500)
//
// }
} }
} }
+31 -17
View File
@@ -12,10 +12,10 @@ struct AppListView: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@AppStorage("settings.general.glass") private var glass: Bool = false @AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280 @AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@Binding var search: String @Binding var search: String
@State private var showSys: Bool = true @State private var showSys: Bool = true
@State private var showUsr: Bool = true @State private var showUsr: Bool = true
// @State private var sidebar: Bool = true
@Binding var showPopover: Bool @Binding var showPopover: Bool
var filteredUserApps: [AppInfo] { var filteredUserApps: [AppInfo] {
@@ -38,14 +38,18 @@ struct AppListView: View {
HStack(alignment: .center, spacing: 0) { HStack(alignment: .center, spacing: 0) {
// App List // App List
if appState.sidebar {
ZStack {
HStack(spacing: 0){ HStack(spacing: 0){
if appState.reload { if appState.reload {
VStack { VStack {
Spacer() Spacer()
ProgressView("Loading apps and files") ProgressView(instantSearch ? "Refreshing app list and caching files" : "Refreshing app list")
if instantSearch {
Image(systemName: "bolt")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
}
Spacer() Spacer()
} }
.frame(width: sidebarWidth) .frame(width: sidebarWidth)
@@ -75,7 +79,6 @@ struct AppListView: View {
Divider().padding(.horizontal, 5) Divider().padding(.horizontal, 5)
} }
} }
// .padding(.bottom)
} }
} }
@@ -104,14 +107,11 @@ struct AppListView: View {
} }
Divider().foregroundStyle(.red)
}
} }
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil) .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.transition(.move(edge: .leading)) .transition(.move(edge: .leading))
Spacer() SlideableDivider(dimension: $sidebarWidth)
}
// Details View // Details View
@@ -130,12 +130,19 @@ struct AppListView: View {
} }
} }
.transition(.move(edge: .leading)) .transition(.move(edge: .leading))
Spacer()
} }
.frame(minWidth: 900, minHeight: 600) .frame(minWidth: 900, minHeight: 600)
.edgesIgnoringSafeArea(.all) .edgesIgnoringSafeArea(.all)
// MARK: Background for whole app // 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(Color("bg").opacity(1))
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all)) // .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
} }
@@ -191,12 +198,12 @@ struct Header: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@Binding var showPopover: Bool @Binding var showPopover: Bool
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
var body: some View { var body: some View {
HStack { HStack {
Text(title).opacity(0.5) Text(title).foregroundStyle(Color("mode")).opacity(0.5)
// Spacer()
HStack { HStack {
if hovered { if hovered {
@@ -214,8 +221,15 @@ struct Header: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = sortedApps.userApps appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps appState.sortedApps.systemApps = sortedApps.systemApps
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations) if instantSearch {
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations) {
appState.reload.toggle() appState.reload.toggle()
}
} else {
appState.reload.toggle()
}
} }
} }
} }
@@ -228,7 +242,6 @@ struct Header: View {
.padding(2) .padding(2)
.background(Color("mode").opacity(0.1)) .background(Color("mode").opacity(0.1))
.clipShape(.capsule) .clipShape(.capsule)
// .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
} }
} }
.onHover { hovering in .onHover { hovering in
@@ -240,6 +253,7 @@ struct Header: View {
Spacer() Spacer()
} }
.padding(4) .padding(.vertical, 8)
.padding(.leading, 6)
} }
} }
-231
View File
@@ -1,231 +0,0 @@
//
// DropTarget.swift
// Pearcleaner
//
// Created by Alin Lupascu on 11/11/23.
//
import Foundation
import SwiftUI
import UniformTypeIdentifiers
struct DropTarget: View, DropDelegate {
@AppStorage("settings.general.ants") private var ants: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false
@ObservedObject var appState: AppState
@ObservedObject var locations: Locations
@State private var shouldFlash = false
private var types: [UTType] = [.fileURL]
@Environment(\.colorScheme) var colorScheme
@State private var phase: CGFloat = 4
@Binding var showPopover: Bool
public init(appState: AppState, locations: Locations, showPopover: Binding<Bool>) {
self.appState = appState
self.locations = locations
_showPopover = showPopover
}
var body: some View {
ZStack {
FlashingBackground(shouldFlash: $shouldFlash)
HStack {
Spacer()
HStack {
Spacer()
ZStack {
// Color("mode")
// .mask(
// Image(mini ? "pear" : "logo")
// .resizable()
// .scaledToFit()
// .frame(minWidth: mini ? 104 : 300)
// .frame(maxWidth: mini ? 104 : 500)
// .padding(mini ? 20 : 10)
// )
LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(mini ? "pear" : "logo")
.resizable()
.scaledToFit()
.frame(minWidth: mini ? 104 : 300)
.frame(maxWidth: mini ? 104 : 500)
.padding(mini ? 20 : 10)
)
}
Spacer()
}
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background {
RoundedRectangle(cornerRadius: 8)
// .fill(LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing))
.strokeBorder(LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing), style: StrokeStyle(lineWidth: 3, dash: [11.0], dashPhase: phase))
.onAppear {
withAnimation(.linear.speed(0.5).repeat(while: ants, autoreverses: false)) {
if ants {
phase -= 22
}
}
}
.onChange(of: ants) { newValue in
withAnimation(.linear.speed(0.5).repeat(while: newValue, autoreverses: false)) {
phase -= 22
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.onDrop(of: types, delegate: self)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.5)) {
appState.currentView = .apps
}
}
.padding(20)
.frame(maxWidth: mini ? 200 : .infinity)
.frame(maxHeight: mini ? 200 : .infinity)
}
func dropEntered(info: DropInfo) {
DispatchQueue.main.async {
self.ants = true
}
}
func dropExited(info: DropInfo) {
DispatchQueue.main.async {
self.ants = false
}
}
func performDrop(info: DropInfo) -> Bool {
let itemProviders = info.itemProviders(for: [UTType.fileURL])
guard itemProviders.count == 1 else {
return false
}
for itemProvider in itemProviders {
itemProvider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { item, error in
guard let data = item as? Data else {
dump(error)
return
}
guard let url = URL(dataRepresentation: data, relativeTo: nil) else {
printOS("Error: Not a valid URL.")
return
}
if url.pathExtension == "app" {
let appInfo = getAppInfo(atPath: url)
// showPopover = false
self.ants = false
showAppInFiles(appInfo: appInfo!, mini: mini, appState: appState, locations: locations, showPopover: $showPopover)
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
// updateOnMain {
// appState.appInfo = .empty
// if let storedAppInfo = appState.appInfoStore.first(where: { $0.path == appInfo!.path }) {
// appState.appInfo = storedAppInfo
// // appState.paths = storedAppInfo.fileSize.keys.map { $0 }//storedAppInfo.files
// appState.selectedItems = Set(storedAppInfo.files)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if mini {
// showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// } else {
// // Handle the case where the appInfo is not found in the store
// printOS("AppInfo not found in the cached store, searching again")
// appState.appInfo = .empty
// appState.appInfo = appInfo!
// findPathsForApp(appInfo: appInfo!, appState: appState, locations: locations)
// withAnimation(Animation.easeIn(duration: 0.4)) {
// if mini {
// showPopover.toggle()
// } else {
// appState.currentView = .files
// }
// }
// }
// }
//
// }
// DispatchQueue.main.async {
// self.ants = false
// appState.appInfo = appInfo!
// findPathsForApp(appState: appState, locations: locations)
// if mini {
// showPopover = true
// } else {
// appState.currentView = .files
// }
// }
} else {
printOS("Error: Dropped file is not an application bundle")
DispatchQueue.main.async {
self.shouldFlash = true
}
}
}
}
DispatchQueue.main.async {
self.ants = false
}
return true
}
}
struct FlashingBackground: View {
@Binding var shouldFlash: Bool
var body: some View {
GeometryReader { geometry in
Rectangle()
.fill(shouldFlash ? Color.red : Color.clear)
.cornerRadius(8)
.padding()
.opacity(shouldFlash ? 0.5 : 0)
.animation(.easeInOut(duration: 0.2).repeatCount(1, autoreverses: true), value: shouldFlash)
.onChange(of: shouldFlash) { newValue in
if newValue {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
shouldFlash = false
}
}
}
.ignoresSafeArea()
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
extension Animation {
func `repeat`(while expression: Bool, autoreverses: Bool = true) -> Animation {
if expression {
return self.repeatForever(autoreverses: autoreverses)
} else {
return self
}
}
}
+45 -71
View File
@@ -21,13 +21,10 @@ struct FilesView: View {
var body: some View { var body: some View {
VStack(alignment: .center) { VStack(alignment: .center) {
if appState.showProgress { //!self.showDetails { if appState.showProgress {
VStack { VStack {
Spacer() Spacer()
// ProgressView("Finding application files..") Text("Searching the file system").font(.title3)
// .progressViewStyle(.linear)
// Spacer()
Text("Almost there, still gathering files..").font(.title3)
.foregroundStyle((.gray.opacity(0.8))) .foregroundStyle((.gray.opacity(0.8)))
ProgressView() ProgressView()
.progressViewStyle(.linear) .progressViewStyle(.linear)
@@ -41,27 +38,29 @@ struct FilesView: View {
VStack() { VStack() {
// Main Group // Main Group
HStack() { HStack(alignment: .center) {
//icon
//app icon, title, size and items
VStack(alignment: .center) {
HStack(alignment: .center) {
if let appIcon = appState.appInfo.appIcon { if let appIcon = appState.appInfo.appIcon {
Image(nsImage: appIcon) Image(nsImage: appIcon)
.resizable() .resizable()
.scaledToFit() .scaledToFit()
// .aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
.padding(.leading) .padding(.trailing)
} }
//app title, size and items
VStack(alignment: .center) {
HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 5){ VStack(alignment: .leading, spacing: 5){
HStack { HStack {
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold) Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold)
// .foregroundStyle(Color("AccentColor"))
Text("").foregroundStyle(Color("AccentColor")) Text("").foregroundStyle(Color("AccentColor"))
Text("\(appState.appInfo.appVersion)").font(.title3) Text("\(appState.appInfo.appVersion)").font(.title3)
// .foregroundStyle(.gray.opacity(0.8)) if appState.appInfo.appName.count < 5 {
InfoButton(text: "Pearcleaner searches for files via a combination of bundle id and app name. \(appState.appInfo.appName) has a common or short app name so there might be unrelated files found. Please check the list thoroughly before uninstalling.", color: nil)
}
} }
Text("\(appState.appInfo.bundleIdentifier)").font(.title3) Text("\(appState.appInfo.bundleIdentifier)").font(.title3)
.foregroundStyle((.gray.opacity(0.8))) .foregroundStyle((.gray.opacity(0.8)))
@@ -69,28 +68,21 @@ struct FilesView: View {
Spacer() Spacer()
VStack(alignment: .trailing, spacing: 5) {
Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold) Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold)
Text("\(appState.appInfo.fileSize.count > 1 ? "\(appState.appInfo.fileSize.count) items" : "\(appState.appInfo.fileSize.count) item")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
}
} }
Divider().padding(.bottom, 7) if appState.appInfo.webApp || appState.appInfo.wrapped {
HStack(alignment: .center, spacing: 10) {
HStack{
Text("Application files and folders")
.font(.callout)
// .foregroundStyle(Color("AccentColor"))
.opacity(0.8)
Spacer() Spacer()
Text("\(appState.appInfo.fileSize.count > 1 ? "\(appState.appInfo.fileSize.count) items" : "\(appState.appInfo.fileSize.count) item")").font(.callout)
// .foregroundStyle(Color("AccentColor").opacity(0.7))
.underline()
}
HStack() {
if appState.appInfo.webApp { if appState.appInfo.webApp {
HStack {
Text("web") Text("web")
.font(.footnote) .font(.footnote)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -98,34 +90,33 @@ struct FilesView: View {
.padding(2) .padding(2)
.background(Color("mode").opacity(0.1)) .background(Color("mode").opacity(0.1))
.clipShape(.capsule) .clipShape(.capsule)
Spacer()
}
if appState.appInfo.wrapped {
Text("iOS")
.font(.footnote)
.foregroundStyle(Color("mode").opacity(0.5))
.frame(minWidth: 30, minHeight: 15)
.padding(2)
.background(Color("mode").opacity(0.1))
.clipShape(.capsule)
} }
} }
if appState.appInfo.appName.count < 5 {
WarningPopoverView(label: "Caution",
bodyText: "Pearcleaner searches for files via a combination of bundle id and app name.\n\(appState.appInfo.appName) has a common or short app name so there might be unrelated files found.\nPlease check the list thoroughly before uninstalling.",
isPresented: $showPop)
} }
}
.padding(.vertical, 5)
} }
.padding(20) .padding(.horizontal, 20)
.padding(.top, 10)
} }
// .padding(.bottom)
.background( Divider()
RoundedRectangle(cornerRadius: 8) .padding()
// .strokeBorder(Color("AccentColor"), lineWidth: 0.5)
.fill(Color("mode").opacity(colorScheme == .dark ? 0.05 : 0.05))
// .background(
// RoundedRectangle(cornerRadius: 8)
// .strokeBorder(Color("AccentColor").opacity(colorScheme == .dark ? 0.1 : 0.1), lineWidth: 1)
// )
)
.padding(.bottom)
ScrollView() { ScrollView() {
VStack { VStack {
@@ -144,29 +135,13 @@ struct FilesView: View {
Divider().padding(.leading, 40) Divider().padding(.leading, 40)
} }
} }
// .padding(.leading, 47).padding(.trailing)
// if let firstPath = appState.appInfo.files.first, path == firstPath {
// FileDetailsItem(size: fileSize, icon: iconImage, path: path)
// .padding(.trailing)
// Divider().padding(.leading, 40).padding(.trailing)
// } else {
// VStack {
// FileDetailsItem(size: fileSize, icon: iconImage, path: path)
// if path != appState.appInfo.files.last {
// Divider().padding(.leading, 40)
// }
// }
// .padding(.leading, 47).padding(.trailing)
// }
} }
} }
} }
.padding() .padding()
} }
// .background(
// RoundedRectangle(cornerRadius: 8) Spacer()
// .fill(Color("mode").opacity(colorScheme == .dark ? 0.05 : 0.05))
// )
HStack() { HStack() {
@@ -202,7 +177,7 @@ struct FilesView: View {
} }
if !appState.selectedItems.isEmpty {
Button("Uninstall") { Button("Uninstall") {
Task { Task {
updateOnMain { updateOnMain {
@@ -215,7 +190,6 @@ struct FilesView: View {
appState.currentView = .empty appState.currentView = .empty
} }
} }
var selectedItemsArray = Array(appState.selectedItems) var selectedItemsArray = Array(appState.selectedItems)
.filter { !$0.path.contains(".Trash") } .filter { !$0.path.contains(".Trash") }
.map { path in .map { path in
@@ -240,9 +214,6 @@ struct FilesView: View {
showPopover = false showPopover = false
updateOnMain { updateOnMain {
appState.isReminderVisible.toggle() appState.isReminderVisible.toggle()
if sentinel {
launchctl(load: true)
}
} }
} }
refreshAppList(appState.appInfo) refreshAppList(appState.appInfo)
@@ -253,7 +224,12 @@ struct FilesView: View {
} }
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Uninstall")) .buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Uninstall"))
.disabled(appState.selectedItems.isEmpty)
} else {
Text("No files selected to clean").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
.padding(5)
}
Spacer() Spacer()
@@ -265,7 +241,6 @@ struct FilesView: View {
.frame(width: 100) .frame(width: 100)
.help("Sorting Selection") .help("Sorting Selection")
} }
.padding(.top)
} }
.transition(.opacity) .transition(.opacity)
@@ -334,7 +309,6 @@ struct FileDetailsItem: View {
.lineLimit(2) .lineLimit(2)
.truncationMode(.tail) .truncationMode(.tail)
.opacity(0.5) .opacity(0.5)
// .foregroundStyle(Color("AccentColor"))
.help(path.path) .help(path.path)
} }
+13 -49
View File
@@ -34,7 +34,6 @@ struct MiniMode: View {
} }
} }
// .padding(.leading, appState.sidebar ? 0 : 10)
.transition(.move(edge: .leading)) .transition(.move(edge: .leading))
.popover(isPresented: $showPopover, arrowEdge: .trailing) { .popover(isPresented: $showPopover, arrowEdge: .trailing) {
VStack { VStack {
@@ -59,7 +58,7 @@ struct MiniMode: View {
} }
.frame(minWidth: 300, minHeight: 335) .frame(minWidth: 300, minHeight: 345)
.edgesIgnoringSafeArea(.all) .edgesIgnoringSafeArea(.all)
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil) .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
// MARK: Background for whole app // MARK: Background for whole app
@@ -87,21 +86,16 @@ struct MiniEmptyView: View {
Spacer() Spacer()
ZStack { ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing) LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask( .mask(
Image(systemName: "plus.square.dashed") Image(systemName: "plus.square.dashed")
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 150, height: 150) .frame(width: 100, height: 100)
.padding() .padding()
) )
} }
// DropTarget(appState: appState, locations: locations, showPopover: $showPopover)
// .frame(maxWidth: mini ? 300 : 500)
Text("Drop your app here") Text("Drop your app here")
.font(.title3) .font(.title3)
.padding(.bottom, 25) .padding(.bottom, 25)
@@ -132,7 +126,6 @@ struct MiniEmptyView: View {
} }
// .padding()
} }
} }
@@ -148,6 +141,7 @@ struct MiniAppView: View {
@State private var showSys: Bool = true @State private var showSys: Bool = true
@State private var showUsr: Bool = true @State private var showUsr: Bool = true
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@Binding var showPopover: Bool @Binding var showPopover: Bool
var body: some View { var body: some View {
@@ -174,37 +168,21 @@ struct MiniAppView: View {
if appState.reload { if appState.reload {
VStack { VStack {
Spacer() Spacer()
ProgressView("Loadings apps and files") ProgressView(instantSearch ? "Refreshing app list and caching files" : "Refreshing app list")
if instantSearch {
Image(systemName: "bolt.fill")
.resizable()
.scaledToFit()
.foregroundStyle(Color("mode").opacity(0.5))
.frame(width: 30, height: 30)
.padding()
}
Spacer() Spacer()
} }
.padding(.vertical) .padding(.vertical)
} else { } else {
VStack(alignment: .center) { VStack(alignment: .center) {
// if appState.currentView == .apps {
// HStack(alignment: .center, spacing: 0) {
// Spacer()
//
// Button("") {
// withAnimation(.easeInOut(duration: 0.5)) {
// // updateOnMain {
// appState.currentView = .empty
// appState.appInfo = AppInfo.empty
// showPopover = false
// // }
// }
// }
// .buttonStyle(SimpleButtonStyle(icon: "arrow.backward.square", help: "Back to Drop Zone", color: Color("mode")))
// .padding(.leading, 10)
// .padding(.trailing, 0)
//
// SearchBarMiniBottom(search: $search)
//
// Spacer()
// }
// .padding(.top, 30)
// }
ScrollView { ScrollView {
LazyVStack(alignment: .leading, pinnedViews: [.sectionHeaders]) { LazyVStack(alignment: .leading, pinnedViews: [.sectionHeaders]) {
@@ -253,20 +231,6 @@ struct MiniAppView: View {
} }
} }
.transition(.move(edge: .leading)) .transition(.move(edge: .leading))
// .popover(isPresented: $showPopover, arrowEdge: .trailing) {
// VStack {
// FilesView(showPopover: $showPopover)
// .id(appState.appInfo.id)
// }
// .background(
// Rectangle()
// .fill(Color("pop"))
//// .background(Color("pop"))
// .padding(-80)
// )
// .frame(minWidth: 600, minHeight: 500)
//
// }
} }
} }
+5 -2
View File
@@ -9,11 +9,11 @@ import Foundation
import SwiftUI import SwiftUI
struct TopBar: View { struct TopBar: View {
// @Binding var sidebar: Bool
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.glass") private var glass: Bool = false @AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false @AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@Binding var showPopover: Bool @Binding var showPopover: Bool
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@@ -50,7 +50,11 @@ struct TopBar: View {
appState.currentView = .zombie appState.currentView = .zombie
appState.showProgress.toggle() appState.showProgress.toggle()
showPopover.toggle() showPopover.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations) reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
} else { } else {
appState.currentView = .zombie appState.currentView = .zombie
showPopover.toggle() showPopover.toggle()
@@ -63,7 +67,6 @@ struct TopBar: View {
} }
// if appState.currentView != .empty || appState.currentView != .apps {
if appState.currentView == .files || appState.currentView == .zombie { if appState.currentView == .files || appState.currentView == .zombie {
Button("") { Button("") {
withAnimation(.easeInOut(duration: 0.5)) { withAnimation(.easeInOut(duration: 0.5)) {
+9 -1
View File
@@ -12,6 +12,7 @@ struct TopBarMini: View {
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false @AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@Binding var search: String @Binding var search: String
@Binding var showPopover: Bool @Binding var showPopover: Bool
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@@ -35,7 +36,11 @@ struct TopBarMini: View {
appState.currentView = .zombie appState.currentView = .zombie
appState.showProgress.toggle() appState.showProgress.toggle()
showPopover.toggle() showPopover.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations) reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
} else { } else {
appState.currentView = .zombie appState.currentView = .zombie
showPopover.toggle() showPopover.toggle()
@@ -70,7 +75,11 @@ struct TopBarMini: View {
appState.currentView = .zombie appState.currentView = .zombie
appState.showProgress.toggle() appState.showProgress.toggle()
showPopover.toggle() showPopover.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations) reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
} else { } else {
appState.currentView = .zombie appState.currentView = .zombie
showPopover.toggle() showPopover.toggle()
@@ -108,7 +117,6 @@ struct SearchBarMini: View {
HStack { HStack {
TextField("Search", text: $search) TextField("Search", text: $search)
.textFieldStyle(AnimatedSearchStyle(text: $search)) .textFieldStyle(AnimatedSearchStyle(text: $search))
// .textFieldStyle(SimpleSearchStyle(trash: true, reload: $reload, text: $search))
} }
.frame(height: 20) .frame(height: 20)
} }
+52 -71
View File
@@ -14,6 +14,7 @@ struct ZombieView: View {
@State private var showPop: Bool = false @State private var showPop: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false @AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@Binding var showPopover: Bool @Binding var showPopover: Bool
@Binding var search: String @Binding var search: String
@@ -21,13 +22,11 @@ struct ZombieView: View {
var body: some View { var body: some View {
VStack(alignment: .center) { VStack(alignment: .center) {
if appState.showProgress { //!self.showDetails { if appState.showProgress {
VStack { VStack {
Spacer() Spacer()
// ProgressView("Finding application files..")
// .progressViewStyle(.linear) Text("Searching the file system").font(.title3)
// Spacer()
Text("Gathering leftover files, this might take a while..").font(.title3)
.foregroundStyle((.gray.opacity(0.8))) .foregroundStyle((.gray.opacity(0.8)))
ProgressView() ProgressView()
.progressViewStyle(.linear) .progressViewStyle(.linear)
@@ -42,73 +41,44 @@ struct ZombieView: View {
// Main Group // Main Group
HStack() { HStack() {
//icon
// if let appIcon = appState.appInfo.appIcon {
// Image(nsImage: appIcon)
// .resizable()
// .scaledToFit()
// // .aspectRatio(contentMode: .fit)
// .frame(width: 50, height: 50)
// .clipShape(RoundedRectangle(cornerRadius: 8))
// .padding(.leading)
// }
//app title, size and items
VStack(alignment: .center) { VStack(alignment: .center) {
HStack(alignment: .center) { HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 5){ Image(systemName: "clock.arrow.circlepath")
HStack(alignment: .center) { .resizable()
Text("Leftover Files").font(.title).fontWeight(.bold) .scaledToFit()
// .foregroundStyle(Color("AccentColor")) .frame(width: 50, height: 50)
// Text("").foregroundStyle(Color("AccentColor")) .symbolRenderingMode(.hierarchical)
// Text("\(appState.appInfo.appVersion)").font(.title3) .padding(.trailing)
// .foregroundStyle(.gray.opacity(0.8))
}
// Text("\(appState.appInfo.bundleIdentifier)").font(.title3)
// .foregroundStyle((.gray.opacity(0.8)))
}
Spacer()
Text("\(formatByte(size: appState.zombieFile.totalSize))").font(.title).fontWeight(.bold)
}
Divider().padding(.bottom, 7)
VStack(alignment: .leading, spacing: 10){
HStack { HStack {
Text("Files and folders remaining from previously installed applications") Text("Leftover Files").font(.title).fontWeight(.bold)
.font(.callout) InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any app bundles to check against. This searches for files/folders and excludes the ones that have overlap with your currently installed apps. Make sure to confirm files marked for removal are correct.", color: .red)
// .foregroundStyle(Color("AccentColor"))
.opacity(0.8)
Spacer() Spacer()
Text("\(appState.zombieFile.fileSize.count > 1 ? "\(appState.zombieFile.fileSize.count) items" : "\(appState.zombieFile.fileSize.count) item")").font(.callout)
// .foregroundStyle(Color("AccentColor").opacity(0.7))
.underline()
}
WarningPopoverView(label: "Caution - Read",
bodyText: "Leftover file search is not 100% accurate as it doesn't have any app bundles to check against.\nThis searches for files/folders and excludes the ones that have overlap with your currently installed apps. \nMake sure to confirm files marked for removal are correct.",
isPresented: $showPop)
.padding(.vertical, 5)
} }
.padding(20) Text("Files and folders remaining from previously installed applications")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
} }
// .padding(.horizontal)
.background(
RoundedRectangle(cornerRadius: 8)
// .strokeBorder(Color("AccentColor"), lineWidth: 0.5)
.fill(Color("mode").opacity(colorScheme == .dark ? 0.05 : 0.05))
// .background(
// RoundedRectangle(cornerRadius: 8)
// .strokeBorder(Color("AccentColor").opacity(colorScheme == .dark ? 0.1 : 0.1), lineWidth: 1)
// )
)
Spacer()
VStack(alignment: .trailing, spacing: 5) {
Text("\(formatByte(size: appState.zombieFile.totalSize))").font(.title).fontWeight(.bold)
Text("\(appState.zombieFile.fileSize.count == 1 ? "\(appState.zombieFile.fileSize.count) item" : "\(appState.zombieFile.fileSize.count) items")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
}
}
}
.padding(.horizontal, 20)
.padding(.top, 10)
}
Divider()
.padding()
ScrollView() { ScrollView() {
LazyVStack { LazyVStack {
@@ -170,16 +140,10 @@ struct ZombieView: View {
} }
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
reversePathsSearch(appState: appState, locations: locations)
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
if !appState.selectedZombieItems.isEmpty {
Button("Remove") { Button("Remove") {
Task { Task {
updateOnMain { updateOnMain {
@@ -214,7 +178,24 @@ struct ZombieView: View {
} }
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Remove")) .buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Remove"))
.disabled(appState.selectedZombieItems.isEmpty) } else {
Text("No files selected to remove").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
.padding(5)
}
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Spacer() Spacer()
-4
View File
@@ -78,10 +78,6 @@ class NewWin: NSWindowController {
return AnyView(PermView() return AnyView(PermView()
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
.environmentObject(appState)) .environmentObject(appState))
// case .restart:
// return AnyView(RestartView()
// .frame(maxWidth: .infinity, maxHeight: .infinity)
// .environmentObject(appState))
} }
} }
} }
-4
View File
@@ -30,10 +30,7 @@ struct PermView: View {
Text("Pearcleaner requires the following permissions:") Text("Pearcleaner requires the following permissions:")
.font(.title3) .font(.title3)
.fontWeight(.semibold) .fontWeight(.semibold)
// .padding(.horizontal, 20)
.padding(.vertical) .padding(.vertical)
// .padding(.bottom)
ScrollView { ScrollView {
Text(" - Full Disk Access permission to find and delete files in non-user locations.\n - Accessibility permission to delete files via Finder which allows for the Undo function.\n\nAdd 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. Restart app when both permissions are granted.") Text(" - Full Disk Access permission to find and delete files in non-user locations.\n - Accessibility permission to delete files via Finder which allows for the Undo function.\n\nAdd 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. Restart app when both permissions are granted.")
@@ -69,7 +66,6 @@ struct PermView: View {
.padding() .padding()
Button(action: { Button(action: {
// NewWin.close
relaunchApp(afterDelay: 1) relaunchApp(afterDelay: 1)
}) { }) {
Text("Restart") Text("Restart")