Skip to content
Merged
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
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 15 additions & 3 deletions Sources/ManagedFavsGenerator/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SwiftUI
import SwiftData
import OSLog
import AppKit

struct ContentView: View {
Expand Down Expand Up @@ -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 {
Expand Down
39 changes: 39 additions & 0 deletions Sources/ManagedFavsGenerator/FaviconProvider.swift
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
27 changes: 26 additions & 1 deletion Sources/ManagedFavsGenerator/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
Expand All @@ -38,7 +63,7 @@ struct SettingsView: View {
}
}
.formStyle(.grouped)
.frame(width: 550, height: 300)
.frame(width: 550, height: 380)
}
}

Expand Down