Add configurable surface/sidebar colour + clearer sync-icon state

- new user_settings.surface_color (nullable, device-local default, not synced):
  drives the web sidebar / top bar / surfaces; NULL derives from bg_color as
  before. Added to schema, migration, GET/PUT, NULLABLE_OVERRIDES, DEFAULT_SYNC.
- web: surface colour row in the settings table; applyTheme derives the whole
  surface family from it
- web: per-row sync icon now has real styling — ON shows the primary-tinted
  glyph on a pill, OFF shows a slashed, muted glyph (was previously unstyled,
  so synced/not-synced looked identical)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-14 22:01:16 +02:00
parent 4ab07ddcc3
commit 131f6b496d
9 changed files with 41 additions and 8 deletions

View File

@@ -3341,6 +3341,8 @@ function populateSettings() {
// ── Settings sync table ───────────────────────────────────
const SYNC_ICON_SVG = '<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>';
// Same sync glyph with a diagonal slash — the clear "not synced" state.
const SYNC_OFF_ICON_SVG = '<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/><path d="M3.2 2.4l18.4 18.4" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" fill="none"/></svg>';
const RESET_ICON_SVG = '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>';
function normalizeHex(raw, fallback) {
@@ -3387,7 +3389,7 @@ function settingRowHtml(def) {
).join('') + '</div>';
}
return `<div class="settings-row" data-row="${def.key}">
<button type="button" class="sync-icon ${synced ? 'on' : ''}" data-sync="${def.key}" role="switch" aria-checked="${synced}" title="${escHtml(t('settings_sync_this'))}">${SYNC_ICON_SVG}</button>
<button type="button" class="sync-icon ${synced ? 'on' : ''}" data-sync="${def.key}" role="switch" aria-checked="${synced}" title="${escHtml(t('settings_sync_this'))}"><span class="si-on">${SYNC_ICON_SVG}</span><span class="si-off">${SYNC_OFF_ICON_SVG}</span></button>
<div class="settings-row-name">${escHtml(t(def.labelKey))}</div>
<div class="settings-row-value">${valueHtml}</div>
</div>`;

View File

@@ -77,6 +77,7 @@ const translations = {
settings_text_color: 'Schriftfarbe',
settings_line_color: 'Linienfarbe',
settings_bg_color: 'Hintergrundfarbe',
settings_surface_color: 'Seitenleisten-/Oberflächenfarbe',
reset: 'Reset',
settings_text_contrast: 'Schriftkontrast',
settings_text_contrast_desc: 'Helligkeit der Beschriftungen und Texte',
@@ -430,6 +431,7 @@ const translations = {
settings_text_color: 'Text color',
settings_line_color: 'Line color',
settings_bg_color: 'Background color',
settings_surface_color: 'Sidebar / surface color',
reset: 'Reset',
settings_text_contrast: 'Text contrast',
settings_text_contrast_desc: 'Brightness of labels and text',

View File

@@ -13,6 +13,7 @@ export const DEFAULT_COLORS = {
text_color: '#FFFFFF',
bg_color: '#000000',
line_color: '#3A3A52',
surface_color: '#1A1A1A',
month_divider_color: '#7090C0',
month_label_color: '#7090C0',
};
@@ -60,6 +61,7 @@ export const SETTING_GROUPS = [
{ key: 'today_color', labelKey: 'settings_today_color', type: 'color' },
{ key: 'text_color', labelKey: 'settings_text_color', type: 'color' },
{ key: 'bg_color', labelKey: 'settings_bg_color', type: 'color' },
{ key: 'surface_color', labelKey: 'settings_surface_color', type: 'color' },
{ key: 'line_color', labelKey: 'settings_line_color', type: 'color' },
{ key: 'month_divider_color', labelKey: 'settings_month_divider_color', type: 'color' },
{ key: 'month_label_color', labelKey: 'settings_month_label_color', type: 'color' },

View File

@@ -132,12 +132,15 @@ export function applyTheme(settings) {
root.style.setProperty('--border', lineColor);
root.style.setProperty('--border-light', shadeHex(lineColor, -0.25));
// Surface family (sidebar / top bar / cards). Explicit surface_color drives
// it; otherwise derive from the app background as before.
const surfaceBase = settings.surface_color || shadeHex(bgColor, 0.10);
root.style.setProperty('--bg-app', bgColor);
root.style.setProperty('--bg-topbar', shadeHex(bgColor, 0.10));
root.style.setProperty('--bg-sidebar', shadeHex(bgColor, 0.10));
root.style.setProperty('--bg-surface', shadeHex(bgColor, 0.18));
root.style.setProperty('--bg-hover', shadeHex(bgColor, 0.26));
root.style.setProperty('--bg-active', shadeHex(bgColor, 0.40));
root.style.setProperty('--bg-topbar', surfaceBase);
root.style.setProperty('--bg-sidebar', surfaceBase);
root.style.setProperty('--bg-surface', shadeHex(surfaceBase, 0.10));
root.style.setProperty('--bg-hover', shadeHex(surfaceBase, 0.20));
root.style.setProperty('--bg-active', shadeHex(surfaceBase, 0.34));
const hh = settings.hour_height || 44;
root.style.setProperty('--hour-h', hh + 'px');