Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions PureMac.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
25E2304A3FF257F1740E304B /* FileProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7953B398E47A4C603DE287F1 /* FileProtection.swift */; };
27F449EDD1B082FE11FEC9DF /* SchedulerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28181F034530331A550C6A3D /* SchedulerService.swift */; };
340E424F759ACCDE7372F99F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5ADDC35F31E40780FB5D017 /* Theme.swift */; };
39BEE5E2BEC526CCADFC9DDD /* ServicesMenu.strings in Resources */ = {isa = PBXBuildFile; fileRef = 57FBBF8C7EF27C8E09126ED5 /* ServicesMenu.strings */; };
41B27CACD7C6C7471EFD03F9 /* UpdateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022D0EAEE2B6E54069FC2CBE /* UpdateService.swift */; };
47C5ECD49C4DD75F271DB6CE /* StringNormalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D3AC5F17D7CA94FAB1E8D7A /* StringNormalization.swift */; };
48D1431A7C99C19EEBDB056B /* CleaningEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8DE5D45BA19E2670B57DC5 /* CleaningEngine.swift */; };
Expand Down Expand Up @@ -44,6 +45,7 @@
B51E5A95BB49A6C0F5273E21 /* FileSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE522B406791BCE905BC55B /* FileSize.swift */; };
B52938BBD11842631314543D /* CategoryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B0AF194677EAC1D5568785 /* CategoryDetailView.swift */; };
B928E4369A3FC4369F014A29 /* SmartCareCoreArtwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAA389C4F50505795FB31A51 /* SmartCareCoreArtwork.swift */; };
BB10A9AFDA394F918E068351 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B55B1419D88851567DFB783A /* InfoPlist.strings */; };
BC6C800216343438413349A3 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D4FD34378988D430A582ED0 /* OnboardingView.swift */; };
CDCDFEBA39A3290101F86AC6 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F510F232341EE18F11DC934 /* MainWindow.swift */; };
CFA64F54CDBF8A4765E0068D /* LocalizationFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155CE1B8CDCCCA6F9FD028C1 /* LocalizationFilesTests.swift */; };
Expand Down Expand Up @@ -81,6 +83,7 @@
10B0AF194677EAC1D5568785 /* CategoryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryDetailView.swift; sourceTree = "<group>"; };
155CE1B8CDCCCA6F9FD028C1 /* LocalizationFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationFilesTests.swift; sourceTree = "<group>"; };
1AB003A7751F05727DBFD1A5 /* AppTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTheme.swift; sourceTree = "<group>"; };
1C0D9A6F75D0B8ADDB32FC2A /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
1D3AC5F17D7CA94FAB1E8D7A /* StringNormalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringNormalization.swift; sourceTree = "<group>"; };
231FF7CBB5F621B66F2AB8A5 /* FDADemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FDADemoView.swift; sourceTree = "<group>"; };
23486A54A82EE3865B784D1C /* AppInfoFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoFetcher.swift; sourceTree = "<group>"; };
Expand All @@ -91,10 +94,13 @@
311078221878708524283765 /* PureMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PureMac.app; sourceTree = BUILT_PRODUCTS_DIR; };
340D303C60FF878B019056B6 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
3667D46D8E2004EB4D73835A /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
374A3BE46E71E54B6927E9EF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ServicesMenu.strings; sourceTree = "<group>"; };
3A8D39C3C250F26302EC45AB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
3D4FD34378988D430A582ED0 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
40A3F22FCEF534DE4A2CAD0E /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = "<group>"; };
40CD4855FB5FA866CAA52C10 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
424F7B28624C271620E13BBC /* EmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateView.swift; sourceTree = "<group>"; };
44392E486177839BA978685E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ServicesMenu.strings; sourceTree = "<group>"; };
46660271CFF167AB0FE7371D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
491771F923C93FD61A263893 /* AppLanguagePreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLanguagePreferencesTests.swift; sourceTree = "<group>"; };
4AE790496C936424EF98320A /* OrphanSafetyPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrphanSafetyPolicy.swift; sourceTree = "<group>"; };
Expand All @@ -105,6 +111,7 @@
60E6BC61614B27C065BB18C9 /* AppListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListView.swift; sourceTree = "<group>"; };
63581B70F9B10231964E3602 /* PureMacApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PureMacApp.swift; sourceTree = "<group>"; };
69B24B648B82186FC3B6EF2B /* BinaryThinner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryThinner.swift; sourceTree = "<group>"; };
6ED5C9C10C69C3143B1F5CA6 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/ServicesMenu.strings; sourceTree = "<group>"; };
752603285F7DBBA12BB3AA91 /* AppStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateTests.swift; sourceTree = "<group>"; };
77D3D9A9BC52839E6D0A22BC /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
7953B398E47A4C603DE287F1 /* FileProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProtection.swift; sourceTree = "<group>"; };
Expand All @@ -124,13 +131,16 @@
B2EA41E1096FA8E3B916AD13 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B71E8F62DB76D85F41F9A2E9 /* OrphanListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrphanListView.swift; sourceTree = "<group>"; };
C0899FE169D270DF95334905 /* LanguageFilesScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageFilesScanner.swift; sourceTree = "<group>"; };
C4332033E243C3571C49A5E9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
C5963EBBEE5BA6E147700821 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
C5ADDC35F31E40780FB5D017 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
CB94E06E145558123BB5BFB3 /* Conditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conditions.swift; sourceTree = "<group>"; };
CED1B71D5F9510582E869CFD /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
DAA389C4F50505795FB31A51 /* SmartCareCoreArtwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCareCoreArtwork.swift; sourceTree = "<group>"; };
DB798781B99987F55D77E2E3 /* SystemMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitor.swift; sourceTree = "<group>"; };
DFC4A1FD7DB92C82725B4314 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
E866A1541D289C69144A5E62 /* FullDiskAccessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullDiskAccessManager.swift; sourceTree = "<group>"; };
ED0C4D8912DA94F81B2AEF8F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
EEF15CB1B8EFCA78EF491824 /* ScanError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanError.swift; sourceTree = "<group>"; };
F0599AA604B0D6BA4F957537 /* ConfettiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfettiView.swift; sourceTree = "<group>"; };
F31F91226CDCFBB8E303B7DA /* AppConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConstants.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -307,10 +317,12 @@
63581B70F9B10231964E3602 /* PureMacApp.swift */,
D4333B07691BD85CAE0E5B15 /* Core */,
7C1729F88C0E5563E1A3DB40 /* Extensions */,
B55B1419D88851567DFB783A /* InfoPlist.strings */,
241E0895B09C71AB423B2F9E /* Localizable.strings */,
F283C00EB52AB140F61500A3 /* Logic */,
3CF46713F75B81F0F86D1C6F /* Models */,
6184B2EC3D01E6E95633406E /* Services */,
57FBBF8C7EF27C8E09126ED5 /* ServicesMenu.strings */,
A8F1C251567E5321E1CA2484 /* ViewModels */,
45B485C4ACB5B08C1B87917F /* Views */,
);
Expand Down Expand Up @@ -411,6 +423,8 @@
ja,
pl,
"pt-BR",
ru,
uk,
"zh-Hans",
"zh-Hant",
);
Expand All @@ -436,7 +450,9 @@
buildActionMask = 2147483647;
files = (
DDD6BA35DBF32E7A6B5F6F8B /* Assets.xcassets in Resources */,
BB10A9AFDA394F918E068351 /* InfoPlist.strings in Resources */,
013EF7B96BF046D4DB38FBC5 /* Localizable.strings in Resources */,
39BEE5E2BEC526CCADFC9DDD /* ServicesMenu.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -527,12 +543,34 @@
5A5C80929EE4A430272674BC /* ja */,
C5963EBBEE5BA6E147700821 /* pl */,
8D5E63D733D1A3BDEDF4DCA5 /* pt-BR */,
C4332033E243C3571C49A5E9 /* ru */,
1C0D9A6F75D0B8ADDB32FC2A /* uk */,
2641C6376DD6F5889F35510E /* zh-Hans */,
02E502E2B5C6AECC76E5CFEF /* zh-Hant */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
57FBBF8C7EF27C8E09126ED5 /* ServicesMenu.strings */ = {
isa = PBXVariantGroup;
children = (
44392E486177839BA978685E /* en */,
6ED5C9C10C69C3143B1F5CA6 /* ru */,
374A3BE46E71E54B6927E9EF /* uk */,
);
name = ServicesMenu.strings;
sourceTree = "<group>";
};
B55B1419D88851567DFB783A /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
40CD4855FB5FA866CAA52C10 /* en */,
ED0C4D8912DA94F81B2AEF8F /* ru */,
DFC4A1FD7DB92C82725B4314 /* uk */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
Expand Down
4 changes: 4 additions & 0 deletions PureMac/Models/AppLanguage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ enum AppLanguage: String, CaseIterable, Identifiable {
case arabic = "ar"
case portugueseBrazil = "pt-BR"
case polish = "pl"
case russian = "ru"
case ukrainian = "uk"
case simplifiedChinese = "zh-Hans"
case traditionalChinese = "zh-Hant"

Expand All @@ -24,6 +26,8 @@ enum AppLanguage: String, CaseIterable, Identifiable {
case .arabic: return "Arabic"
case .polish: return "Polish"
case .portugueseBrazil: return "Portuguese (Brazil)"
case .russian: return "Russian"
case .ukrainian: return "Ukrainian"
case .simplifiedChinese: return "Chinese (Simplified)"
case .traditionalChinese: return "Chinese (Traditional)"
}
Expand Down
2 changes: 1 addition & 1 deletion PureMac/PureMacApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let urls = (pboard.readObjects(forClasses: [NSURL.self],
options: [.urlReadingFileURLsOnly: true]) as? [URL]) ?? []
guard let appURL = urls.first(where: { $0.pathExtension == "app" }) else {
error?.pointee = "Select an application (.app) to uninstall." as NSString
error?.pointee = String(localized: "Select an application (.app) to uninstall.") as NSString
return
}
NSApp.activate(ignoringOtherApps: true)
Expand Down
2 changes: 1 addition & 1 deletion PureMac/Services/MenuBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class MenuBarController: NSObject, NSPopoverDelegate {
if let button = statusItem.button {
button.image = NSImage(
systemSymbolName: "gauge.with.dots.needle.67percent",
accessibilityDescription: "System Monitor"
accessibilityDescription: String(localized: "System Monitor")
)
button.imagePosition = .imageLeading
button.target = self
Expand Down
24 changes: 12 additions & 12 deletions PureMac/Services/ScanEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -226,37 +226,37 @@ actor ScanEngine {
private func scanAIApps() -> CategoryResult {
let targets = [
CleanupTarget(
name: "Ollama Logs",
name: String(localized: "Ollama Logs"),
path: "\(home)/.ollama/logs"
),
CleanupTarget(
name: "Ollama Cache",
name: String(localized: "Ollama Cache"),
path: "\(home)/Library/Caches/ollama"
),
CleanupTarget(
name: "Ollama Electron Cache",
name: String(localized: "Ollama Electron Cache"),
path: "\(home)/Library/Caches/com.electron.ollama"
),
CleanupTarget(
name: "Ollama WebKit Data",
name: String(localized: "Ollama WebKit Data"),
path: "\(home)/Library/WebKit/com.electron.ollama"
),
CleanupTarget(
name: "Ollama Saved State",
name: String(localized: "Ollama Saved State"),
path: "\(home)/Library/Saved Application State/com.electron.ollama.savedState"
),
CleanupTarget(
name: "Ollama CLI Prompt History (Optional)",
name: String(localized: "Ollama CLI Prompt History (Optional)"),
path: "\(home)/.ollama/history",
isSelected: false,
minimumSize: 0
),
CleanupTarget(
name: "LM Studio Server Logs",
name: String(localized: "LM Studio Server Logs"),
path: "\(home)/.lmstudio/server-logs"
),
CleanupTarget(
name: "LM Studio Conversations (Optional)",
name: String(localized: "LM Studio Conversations (Optional)"),
path: "\(home)/.lmstudio/conversations",
isSelected: false,
minimumSize: 0
Expand Down Expand Up @@ -572,12 +572,12 @@ actor ScanEngine {

let managers: [ManagerCache] = [
ManagerCache(
name: "npm cache",
name: String(localized: "npm cache"),
defaultPath: "\(home)/.npm",
detectionCommand: (cli: "npm", args: ["config", "get", "cache"])
),
ManagerCache(
name: "yarn classic cache",
name: String(localized: "yarn classic cache"),
defaultPath: "\(home)/Library/Caches/Yarn",
detectionCommand: (cli: "yarn", args: ["cache", "dir"])
),
Expand All @@ -586,7 +586,7 @@ actor ScanEngine {
// touched by a system cleaner. The classic cache above remains the
// global, safe-to-clean location.
ManagerCache(
name: "pnpm content-addressable store",
name: String(localized: "pnpm content-addressable store"),
defaultPath: "\(home)/Library/pnpm/store",
detectionCommand: (cli: "pnpm", args: ["store", "path"])
),
Expand Down Expand Up @@ -735,7 +735,7 @@ actor ScanEngine {
for dockerBin in dockerBinPaths where fileManager.fileExists(atPath: dockerBin) {
if let reclaimable = reclaimableDockerSpace(dockerBin: dockerBin), reclaimable > 0 {
items.append(CleanableItem(
name: "Docker prune (stopped containers, dangling images, build cache)",
name: String(localized: "Docker prune (stopped containers, dangling images, build cache)"),
path: "",
size: reclaimable,
category: .dockerCache,
Expand Down
2 changes: 1 addition & 1 deletion PureMac/Views/Components/DashboardCharts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ struct CategoryBarChart: View {
return Chart(bars) { bar in
BarMark(
x: .value("Size", Double(bar.size) * reveal),
y: .value("Category", bar.name)
y: .value(String(localized: "Category"), bar.name)
)
.foregroundStyle(bar.category.color.gradient)
.cornerRadius(5)
Expand Down
2 changes: 1 addition & 1 deletion PureMac/Views/Components/FDADemoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ struct FDADemoView: View {
}

private struct DemoRow {
let name: String
let name: LocalizedStringKey
let systemImage: String
let granted: Bool
var isPureMac: Bool = false
Expand Down
10 changes: 6 additions & 4 deletions PureMac/Views/Components/PermissionSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ struct PermissionSheet: View {
VStack(alignment: .leading, spacing: 2) {
Text(coordinator.context.headline)
.font(.system(size: 16, weight: .bold))
Text(coordinator.hasFullDiskAccess
? "Access granted. Retrying…"
: "1-tap setup. We'll detect the change automatically.")
Text(LocalizedStringKey(
coordinator.hasFullDiskAccess
? "Access granted. Retrying…"
: "1-tap setup. We'll detect the change automatically."
))
.font(.system(size: 12))
.foregroundStyle(.secondary)
}
Expand Down Expand Up @@ -160,7 +162,7 @@ struct PermissionSheet: View {
.font(.system(size: 11.5))
.foregroundStyle(.secondary)
Spacer()
Button(showAdvanced ? "Hide help" : "PureMac not in the list?") {
Button(LocalizedStringKey(showAdvanced ? "Hide help" : "PureMac not in the list?")) {
withAnimation(.easeInOut(duration: 0.25)) { showAdvanced.toggle() }
}
.buttonStyle(.link)
Expand Down
Loading