From 7ea50d312373028eb27df56fb3e81a8ce4c48d27 Mon Sep 17 00:00:00 2001 From: Franz Kafka Date: Mon, 23 Mar 2026 14:22:24 +0000 Subject: [PATCH] feat(ui): make favicons user-configurable, off by default - Add favicon service preference: None (default), Google, DuckDuckGo - result_item.html: remove hardcoded Google favicon src, defer to JS - applyFavicon() reads data-domain attr and sets src or display:none - Privacy-by-default: users must explicitly opt in to any favicon service - Add favicon selector to both the settings panel and preferences page --- internal/views/static/js/settings.js | 50 ++++++++++++++++++++++- internal/views/templates/preferences.html | 11 +++++ internal/views/templates/result_item.html | 2 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/internal/views/static/js/settings.js b/internal/views/static/js/settings.js index 9682e6a..324d35f 100644 --- a/internal/views/static/js/settings.js +++ b/internal/views/static/js/settings.js @@ -7,7 +7,8 @@ var DEFAULT_PREFS = { theme: 'system', engines: ALL_ENGINES.slice(), safeSearch: 'moderate', - format: 'html' + format: 'html', + favicon: 'none' // 'none' | 'google' | 'duckduckgo' }; var STORAGE_KEY = 'kafka_prefs'; @@ -22,7 +23,8 @@ function loadPrefs() { theme: parsed.theme || DEFAULT_PREFS.theme, engines: parsed.engines || DEFAULT_PREFS.engines.slice(), safeSearch: parsed.safeSearch || DEFAULT_PREFS.safeSearch, - format: parsed.format || DEFAULT_PREFS.format + format: parsed.format || DEFAULT_PREFS.format, + favicon: parsed.favicon || DEFAULT_PREFS.favicon }; } catch (e) { prefs = DEFAULT_PREFS; @@ -43,6 +45,24 @@ function applyTheme(theme) { } } +function applyFavicon(service) { + var faviconMap = { + google: function(domain) { return 'https://www.google.com/s2/favicons?domain=' + encodeURIComponent(domain) + '&sz=32'; }, + duckduckgo: function(domain) { return 'https://icons.duckduckgo.com/ip3/' + encodeURIComponent(domain) + '.ico'; } + }; + var imgs = document.querySelectorAll('.result-favicon'); + imgs.forEach(function(img) { + var domain = img.getAttribute('data-domain'); + if (!domain) return; + if (service === 'none') { + img.style.display = 'none'; + } else { + img.style.display = ''; + img.src = faviconMap[service](domain); + } + }); +} + function syncEngineInput(prefs) { var input = document.getElementById('engines-input'); if (input) { @@ -104,6 +124,13 @@ function renderPanel(prefs) { }); + var faviconOptions = ''; + ['none', 'google', 'duckduckgo'].forEach(function(src) { + var labels = { none: 'None', google: 'Google', duckduckgo: 'DuckDuckGo' }; + var selected = prefs.favicon === src ? ' selected' : ''; + faviconOptions += ''; + }); + body.innerHTML = '
' + '
Appearance
' + @@ -124,6 +151,10 @@ function renderPanel(prefs) { '' + '' + '
' + + '
' + + '' + + '' + + '
' + ''; // Theme buttons @@ -162,6 +193,7 @@ function renderPanel(prefs) { function initSettings() { var prefs = loadPrefs(); applyTheme(prefs.theme); + applyFavicon(prefs.favicon); syncEngineInput(prefs); renderPanel(prefs); @@ -257,6 +289,9 @@ function initPreferences() { // Load saved preferences var prefs = loadPrefs(); + // Apply favicon settings immediately on preferences page + applyFavicon(prefs.favicon); + // Theme var themeEl = document.getElementById('pref-theme'); if (themeEl) { @@ -288,6 +323,17 @@ function initPreferences() { }); } + // Favicon service (if exists on page) + var faviconEl = document.getElementById('pref-favicon'); + if (faviconEl) { + faviconEl.value = prefs.favicon || 'none'; + faviconEl.addEventListener('change', function() { + prefs.favicon = faviconEl.value; + savePrefs(prefs); + applyFavicon(prefs.favicon); + }); + } + // Show first section by default showSection('search'); } diff --git a/internal/views/templates/preferences.html b/internal/views/templates/preferences.html index e67178b..53a90e3 100644 --- a/internal/views/templates/preferences.html +++ b/internal/views/templates/preferences.html @@ -69,6 +69,17 @@ +
+
+ +

Fetch favicons for result URLs. "None" is most private.

+
+ +
diff --git a/internal/views/templates/result_item.html b/internal/views/templates/result_item.html index 3967891..33f5e8c 100644 --- a/internal/views/templates/result_item.html +++ b/internal/views/templates/result_item.html @@ -4,7 +4,7 @@ {{.SafeTitle}}
- + {{.URL}} {{.Engine}}