diff --git a/README.md b/README.md index 876ae55..1f7f29b 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,32 @@ The app generates a complete macOS Configuration Profile with: - βœ… **Export fails**: Verify write permissions for target directory - βœ… **App won't start**: Ensure macOS 15+ and try rebuilding +## πŸ” Debugging & Verification + +### Verify Favicon Provider + +You can verify which favicon provider (Google or DuckDuckGo) the app is using in real-time: + +```bash +# Live stream of favicon loading logs +log stream --predicate 'subsystem == "ManagedFavsGenerator" AND category == "Favicons"' --level info --style compact +``` + +**Example output:** +``` +Loading favicon for 'github.com' using Google provider: https://www.google.com/s2/favicons?domain=github.com&sz=32 +Loading favicon for 'microsoft.com' using DuckDuckGo provider: https://icons.duckduckgo.com/ip3/microsoft.com.ico +``` + +**To change the provider:** +1. Open Settings (⌘,) +2. Navigate to **Appearance** section +3. Select your preferred **Favicon Provider**: + - **Google**: More reliable, comprehensive coverage + - **DuckDuckGo**: Privacy-focused, no tracking + +Changes take effect immediately without restart. + ## πŸ› οΈ Technical Details For developers and technical documentation, see **[AGENTS.md](../AGENTS.md)** - Development guidelines, architecture, and best practices. diff --git a/Sources/ManagedFavsGenerator/ContentView.swift b/Sources/ManagedFavsGenerator/ContentView.swift index 9b9c6c3..654d704 100644 --- a/Sources/ManagedFavsGenerator/ContentView.swift +++ b/Sources/ManagedFavsGenerator/ContentView.swift @@ -1,5 +1,6 @@ import SwiftUI import SwiftData +import OSLog import AppKit struct ContentView: View { @@ -345,16 +346,27 @@ struct FavoriteRowView: View { let onRemove: () -> Void @State private var isHovering = false @State private var isDragging = false + @AppStorage("faviconProvider") private var faviconProvider: FaviconProvider = .google - /// Generates the favicon URL using Google's favicon service + private let logger = Logger(subsystem: "ManagedFavsGenerator", category: "Favicons") + + /// Generates the favicon URL using the selected provider private var faviconURL: URL? { guard let urlString = favorite.url, !urlString.isEmpty, let url = URL(string: urlString), - let domain = url.host else { + let host = url.host else { return nil } - return URL(string: "https://www.google.com/s2/favicons?domain=\(domain)&sz=32") + + // Remove www. prefix if present + let domain = host.hasPrefix("www.") ? String(host.dropFirst(4)) : host + let faviconURL = faviconProvider.faviconURL(for: domain) + + // Log which provider is being used (public for debugging) + logger.info("Loading favicon for '\(domain, privacy: .public)' using \(faviconProvider.rawValue, privacy: .public) provider: \(faviconURL?.absoluteString ?? "nil", privacy: .public)") + + return faviconURL } var body: some View { diff --git a/Sources/ManagedFavsGenerator/FaviconProvider.swift b/Sources/ManagedFavsGenerator/FaviconProvider.swift new file mode 100644 index 0000000..ef29888 --- /dev/null +++ b/Sources/ManagedFavsGenerator/FaviconProvider.swift @@ -0,0 +1,39 @@ +import Foundation + +/// Favicon service provider +enum FaviconProvider: String, CaseIterable, Identifiable, Codable { + case google = "Google" + case duckduckgo = "DuckDuckGo" + + var id: String { rawValue } + + /// Display name for UI + var displayName: String { + switch self { + case .google: + return "Google" + case .duckduckgo: + return "DuckDuckGo (Privacy)" + } + } + + /// Description for settings + var description: String { + switch self { + case .google: + return "More reliable, comprehensive coverage" + case .duckduckgo: + return "Privacy-focused, no tracking" + } + } + + /// Generate favicon URL for a given domain + func faviconURL(for domain: String) -> URL? { + switch self { + case .google: + return URL(string: "https://www.google.com/s2/favicons?domain=\(domain)&sz=32") + case .duckduckgo: + return URL(string: "https://icons.duckduckgo.com/ip3/\(domain).ico") + } + } +} diff --git a/Sources/ManagedFavsGenerator/SettingsView.swift b/Sources/ManagedFavsGenerator/SettingsView.swift index 6e57e8a..9250fe7 100644 --- a/Sources/ManagedFavsGenerator/SettingsView.swift +++ b/Sources/ManagedFavsGenerator/SettingsView.swift @@ -3,6 +3,7 @@ import SwiftUI /// Settings View fΓΌr App-Einstellungen struct SettingsView: View { @State private var viewModel = FavoritesViewModel() + @AppStorage("faviconProvider") private var faviconProvider: FaviconProvider = .google var body: some View { Form { @@ -23,6 +24,30 @@ struct SettingsView: View { Divider() + Section { + Picker("Favicon Provider", selection: $faviconProvider) { + ForEach(FaviconProvider.allCases) { provider in + Text(provider.displayName).tag(provider) + } + } + .help("Choose which service to use for loading favicons") + + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 8) { + Image(systemName: "info.circle") + .foregroundStyle(.secondary) + .imageScale(.small) + Text(faviconProvider.description) + .font(.caption) + .foregroundStyle(.secondary) + } + } + } header: { + Text("Appearance") + } + + Divider() + Section { LabeledContent("Version") { Text("1.1.0") @@ -38,7 +63,7 @@ struct SettingsView: View { } } .formStyle(.grouped) - .frame(width: 550, height: 300) + .frame(width: 550, height: 380) } }