diff --git a/internal/views/static/js/settings.js b/internal/views/static/js/settings.js
new file mode 100644
index 0000000..77c9f7a
--- /dev/null
+++ b/internal/views/static/js/settings.js
@@ -0,0 +1,266 @@
+var ALL_ENGINES = [
+ 'wikipedia', 'arxiv', 'crossref', 'braveapi',
+ 'qwant', 'duckduckgo', 'github', 'reddit', 'bing'
+];
+
+var DEFAULT_PREFS = {
+ theme: 'system',
+ engines: ALL_ENGINES.slice(),
+ safeSearch: 'moderate',
+ format: 'html'
+};
+
+var STORAGE_KEY = 'kafka_prefs';
+
+function loadPrefs() {
+ var stored = localStorage.getItem(STORAGE_KEY);
+ var prefs = DEFAULT_PREFS;
+ if (stored) {
+ try {
+ var parsed = JSON.parse(stored);
+ prefs = {
+ 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
+ };
+ } catch (e) {
+ prefs = DEFAULT_PREFS;
+ }
+ }
+ return prefs;
+}
+
+function savePrefs(prefs) {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
+}
+
+function applyTheme(theme) {
+ if (theme === 'system') {
+ document.documentElement.removeAttribute('data-theme');
+ } else {
+ document.documentElement.setAttribute('data-theme', theme);
+ }
+}
+
+function syncEngineInput(prefs) {
+ var input = document.getElementById('engines-input');
+ if (input) {
+ input.value = prefs.engines.join(',');
+ }
+}
+
+function closePanel() {
+ var popover = document.getElementById('settings-popover');
+ var trigger = document.getElementById('settings-trigger');
+ if (popover) {
+ popover.setAttribute('data-open', 'false');
+ }
+ if (trigger) {
+ trigger.setAttribute('aria-expanded', 'false');
+ }
+ if (trigger) {
+ trigger.focus();
+ }
+}
+
+function openPanel() {
+ var popover = document.getElementById('settings-popover');
+ var trigger = document.getElementById('settings-trigger');
+ if (popover) {
+ popover.setAttribute('data-open', 'true');
+ }
+ if (trigger) {
+ trigger.setAttribute('aria-expanded', 'true');
+ }
+ var firstFocusable = popover ? popover.querySelector('button, input, select, textarea, a[href], [tabindex]:not([tabindex="-1"])') : null;
+ if (firstFocusable) {
+ firstFocusable.focus();
+ }
+}
+
+function escapeHtml(str) {
+ return str
+ .replace(/&/g, '&')
+ .replace(//g, '>');
+}
+
+function renderPanel(prefs) {
+ var themeIcons = { light: '☀️', dark: '🌙', system: '⌨' };
+ var safeOptions = ['moderate', 'strict', 'off'];
+ var formatOptions = ['html', 'json', 'csv', 'rss'];
+
+ var html = '
';
+ html += '
Settings
';
+
+ // Theme buttons
+ html += '
';
+
+ // Engine checkboxes
+ html += '
';
+
+ html += '
Engine changes apply to your next search.
';
+
+ // Safe search dropdown
+ html += '
';
+ html += '
';
+
+ // Format dropdown
+ html += '
';
+ html += '
';
+
+ html += '
';
+ html += '
';
+
+ document.body.innerHTML = html;
+
+ // Attach listeners to theme buttons
+ var themeButtons = document.querySelectorAll('input[name="theme"]');
+ themeButtons.forEach(function(btn) {
+ btn.addEventListener('click', function() {
+ var newPrefs = loadPrefs();
+ newPrefs.theme = btn.value;
+ savePrefs(newPrefs);
+ applyTheme(newPrefs.theme);
+ });
+ });
+
+ // Attach listeners to engine checkboxes
+ var engineCheckboxes = document.querySelectorAll('input[name="engine"]');
+ engineCheckboxes.forEach(function(cb) {
+ cb.addEventListener('change', function() {
+ var newPrefs = loadPrefs();
+ if (cb.checked) {
+ if (newPrefs.engines.indexOf(cb.value) === -1) {
+ newPrefs.engines.push(cb.value);
+ }
+ } else {
+ newPrefs.engines = newPrefs.engines.filter(function(e) { return e !== cb.value; });
+ }
+ savePrefs(newPrefs);
+ syncEngineInput(newPrefs);
+ });
+ });
+
+ // Attach listener to safe search dropdown
+ var safeSelect = document.getElementById('safe-search-select');
+ if (safeSelect) {
+ safeSelect.addEventListener('change', function() {
+ var newPrefs = loadPrefs();
+ newPrefs.safeSearch = safeSelect.value;
+ savePrefs(newPrefs);
+ });
+ }
+
+ // Attach listener to format dropdown
+ var formatSelect = document.getElementById('format-select');
+ if (formatSelect) {
+ formatSelect.addEventListener('change', function() {
+ var newPrefs = loadPrefs();
+ newPrefs.format = formatSelect.value;
+ savePrefs(newPrefs);
+ });
+ }
+
+ // Attach listener to close button
+ var closeBtn = document.getElementById('settings-close');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', closePanel);
+ }
+}
+
+function initSettings() {
+ var prefs = loadPrefs();
+ applyTheme(prefs.theme);
+ syncEngineInput(prefs);
+ renderPanel(prefs);
+
+ var trigger = document.getElementById('settings-trigger');
+ var triggerMobile = document.getElementById('settings-trigger-mobile');
+
+ function togglePanel() {
+ var popover = document.getElementById('settings-popover');
+ if (popover && popover.getAttribute('data-open') === 'true') {
+ closePanel();
+ } else {
+ openPanel();
+ }
+ }
+
+ if (trigger) {
+ trigger.addEventListener('click', togglePanel);
+ }
+ if (triggerMobile) {
+ triggerMobile.addEventListener('click', togglePanel);
+ }
+}
+
+// Escape key handler
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ var popover = document.getElementById('settings-popover');
+ if (popover && popover.getAttribute('data-open') === 'true') {
+ closePanel();
+ }
+ }
+});
+
+// Click outside handler
+document.addEventListener('click', function(e) {
+ var popover = document.getElementById('settings-popover');
+ var trigger = document.getElementById('settings-trigger');
+ if (popover && popover.getAttribute('data-open') === 'true') {
+ if (!popover.contains(e.target) && (!trigger || !trigger.contains(e.target))) {
+ closePanel();
+ }
+ }
+});
+
+// Focus trap
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'Tab') {
+ var popover = document.getElementById('settings-popover');
+ if (popover && popover.getAttribute('data-open') === 'true') {
+ var focusableElements = popover.querySelectorAll('button, input, select, textarea, a[href], [tabindex]:not([tabindex="-1"])');
+ var firstEl = focusableElements[0];
+ var lastEl = focusableElements[focusableElements.length - 1];
+ if (e.shiftKey && document.activeElement === firstEl) {
+ e.preventDefault();
+ lastEl.focus();
+ } else if (!e.shiftKey && document.activeElement === lastEl) {
+ e.preventDefault();
+ firstEl.focus();
+ }
+ }
+ }
+});
+
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initSettings);
+} else {
+ initSettings();
+}