// ── Theme + Settings ──
import { $, api, getCsrf } from './util.js';
import { state } from './state.js';
import { renderSettingsPanel as renderNotifSettings } from './notifications.js';
const overlay = $('settings-overlay');
const themeList = $('settings-theme-list');
// Render theme list in settings
function renderThemes() {
if (!themeList) return;
const current = (designPending?.theme) || localStorage.getItem('hyprcrd-theme') || 'slate';
themeList.innerHTML = '';
for (const [key, t] of Object.entries(window.themes)) {
const btn = document.createElement('button');
const active = key === current;
btn.className = `flex items-center gap-3 px-3 py-2.5 text-left text-xs border transition-colors rounded ${active ? 'border-t-accent text-t-bright' : 'border-t-border text-t-muted hover:border-t-accent hover:text-t-bright'}`;
btn.innerHTML = `
${t.label}
${active ? '' : ''}`;
btn.onclick = () => {
window.applyTheme(key);
if (designPending) {
designPending.theme = key;
window.saveDesign(designPending);
flashSaved();
}
renderThemes();
window._renderBoard?.();
};
themeList.appendChild(btn);
}
}
// Tab switching
overlay?.querySelectorAll('.settings-tab').forEach(tab => {
tab.addEventListener('click', () => {
overlay.querySelectorAll('.settings-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
const panel = tab.dataset.tab;
overlay.querySelectorAll('[id^="settings-panel-"]').forEach(p => p.classList.add('hidden'));
$(`settings-panel-${panel}`)?.classList.remove('hidden');
if (panel === 'caldav') { window._loadCaldavSettings?.(); }
if (panel === 'relay') { window._loadRelaySettings?.(); }
if (panel === 'notifications') renderNotifSettings();
if (panel === 'behavior') initBehaviorSettings();
if (panel === 'theme') initDesignSettings();
if (panel === 'plugins') window._openPluginSettings?.();
if (panel !== 'theme') setPeekUI(false);
});
});
// ── Design (fonts, geometry, borders, spacing, effects, presets) ──
let designPending = null;
const SLIDER_FIELDS = [
['setting-radius-card', 'radiusCard', 'val-radius-card'],
['setting-radius-button', 'radiusButton', 'val-radius-button'],
['setting-radius-input', 'radiusInput', 'val-radius-input'],
['setting-radius-modal', 'radiusModal', 'val-radius-modal'],
['setting-radius-tag', 'radiusTag', 'val-radius-tag'],
['setting-radius-image', 'radiusImage', 'val-radius-image'],
['setting-border-card', 'borderCard', 'val-border-card'],
['setting-bar-height', 'barHeight', 'val-bar-height'],
['setting-pad-card', 'padCard', 'val-pad-card'],
['setting-gap-board', 'gapBoard', 'val-gap-board'],
['setting-lift-card', 'liftCard', 'val-lift-card'],
['setting-blur-overlay', 'blurOverlay', 'val-blur-overlay'],
];
const SELECT_FIELDS = [
['setting-font-headline', 'fontHeadline'],
['setting-font-content', 'fontContent'],
['setting-font-ui', 'fontUi'],
['setting-border-style', 'borderStyle'],
['setting-shadow-card', 'shadowCard'],
['setting-shadow-modal', 'shadowModal'],
['setting-shadow-ui', 'shadowUi'],
['setting-anim-speed', 'animSpeed'],
];
function updatePreview() {
const preview = $('theme-preview');
if (!preview || !designPending) return;
const d = designPending, fonts = window.themeFonts, shadows = window.designShadows;
const set = (k, v) => preview.style.setProperty(k, v);
set('--tp-font-headline', fonts[d.fontHeadline] || fonts.pixel);
set('--tp-font-content', fonts[d.fontContent] || fonts.mono);
set('--tp-font-ui', fonts[d.fontUi] || fonts.pixel);
set('--tp-radius-card', d.radiusCard + 'px');
set('--tp-radius-button', d.radiusButton + 'px');
set('--tp-radius-input', d.radiusInput + 'px');
set('--tp-radius-tag', d.radiusTag + 'px');
set('--tp-border-card', d.borderCard + 'px');
set('--tp-border-style', d.borderStyle);
set('--tp-bar-height', d.barHeight + 'px');
set('--tp-pad-card', d.padCard + 'px');
set('--tp-shadow-card', shadows[d.shadowCard] || 'none');
}
function fillFontSelect(id, value) {
const el = $(id);
if (!el) return;
el.innerHTML = '';
for (const [key, label] of Object.entries(window.themeFontLabels)) {
const opt = document.createElement('option');
opt.value = key; opt.textContent = label;
if (key === value) opt.selected = true;
el.appendChild(opt);
}
}
function syncControlsFromPending() {
if (!designPending) return;
fillFontSelect('setting-font-headline', designPending.fontHeadline);
fillFontSelect('setting-font-content', designPending.fontContent);
fillFontSelect('setting-font-ui', designPending.fontUi);
for (const [id, key] of SELECT_FIELDS) {
if (id.startsWith('setting-font-')) continue;
const el = $(id);
if (el) el.value = String(designPending[key] ?? '');
}
for (const [id, key, valId] of SLIDER_FIELDS) {
const v = String(designPending[key] ?? 0);
const el = $(id);
if (el) el.value = v;
const valEl = $(valId);
if (valEl) valEl.textContent = v;
}
}
function commitDesign() {
updatePreview();
window.applyDesign(designPending);
window.saveDesign(designPending);
flashSaved();
}
let saveFlashTimer = null;
function flashSaved() {
const status = $('design-status');
if (!status) return;
status.textContent = 'saved';
status.style.color = 'var(--t-accent)';
clearTimeout(saveFlashTimer);
saveFlashTimer = setTimeout(() => {
status.textContent = 'auto-saving';
status.style.color = 'var(--t-dim)';
}, 700);
}
let designWired = false;
function initDesignSettings() {
designPending = window.loadDesign();
syncControlsFromPending();
updatePreview();
refreshPresetSelect();
const status = $('design-status');
if (status) { status.textContent = 'auto-saving'; status.style.color = 'var(--t-dim)'; }
if (designWired) return;
designWired = true;
// Selects
for (const [id, key] of SELECT_FIELDS) {
const el = $(id);
if (!el) continue;
el.addEventListener('change', () => {
designPending[key] = el.value;
commitDesign();
});
}
// Sliders
for (const [id, key, valId] of SLIDER_FIELDS) {
const el = $(id);
if (!el) continue;
el.addEventListener('input', () => {
const num = Number(el.value);
designPending[key] = num;
const v = $(valId);
if (v) v.textContent = num;
commitDesign();
});
}
}
$('design-reset')?.addEventListener('click', () => {
designPending = { ...window.designDefaults };
syncControlsFromPending();
commitDesign();
renderThemes();
});
// ── Peek toggle (hide background dim so the real board is visible behind the popup) ──
function setPeekUI(peeking) {
document.body.classList.toggle('settings-peeking', peeking);
const sw = $('design-peek-toggle');
if (sw) sw.classList.toggle('on', peeking);
}
const peekRow = $('design-peek-row');
if (peekRow) peekRow.onclick = () => {
setPeekUI(!document.body.classList.contains('settings-peeking'));
};
// ── Presets ──
function loadUserPresets() {
try { return JSON.parse(localStorage.getItem('hyprcrd-design-presets') || '{}'); }
catch { return {}; }
}
function saveUserPresets(p) {
localStorage.setItem('hyprcrd-design-presets', JSON.stringify(p));
}
function refreshPresetSelect() {
const sel = $('design-preset-select');
if (!sel) return;
// localStorage wins over sel.value: the