Changeset 0.25.3 (#163)

This commit is contained in:
2026-03-09 01:54:06 +00:00
parent 6c484fa7f8
commit 2f7a0fb027
11 changed files with 235 additions and 74 deletions

View File

@@ -172,36 +172,39 @@ Object.assign(UI, {
},
initAppearance() {
// Load saved prefs on startup
const prefs = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
UI.applyAppearance(prefs.scale || 100, prefs.msgFont || 14);
UI.applyTheme(prefs.theme || 'system');
// Theme and appearance are already applied by the universal init
// in base.html (Theme.init() + UI.restoreAppearance()). This method
// only wires up the settings UI controls.
// Live preview on slider change
// Scale slider: show value live, but apply zoom only on release to avoid
// jarring layout jumps mid-drag (zoom changes the layout viewport).
const scaleEl = document.getElementById('settingsScale');
const msgFontEl = document.getElementById('settingsMsgFont');
if (scaleEl) scaleEl.addEventListener('input', () => {
const v = parseInt(scaleEl.value);
document.getElementById('scaleValue').textContent = v + '%';
UI.applyAppearance(v, parseInt(msgFontEl?.value || 14));
});
if (msgFontEl) msgFontEl.addEventListener('input', () => {
const v = parseInt(msgFontEl.value);
document.getElementById('msgFontValue').textContent = v + 'px';
UI.applyAppearance(parseInt(scaleEl?.value || 100), v);
});
if (scaleEl) {
scaleEl.addEventListener('input', () => {
document.getElementById('scaleValue').textContent = parseInt(scaleEl.value) + '%';
});
scaleEl.addEventListener('change', () => {
UI.applyAppearance(parseInt(scaleEl.value), parseInt(msgFontEl?.value || 14));
});
}
if (msgFontEl) {
msgFontEl.addEventListener('input', () => {
document.getElementById('msgFontValue').textContent = parseInt(msgFontEl.value) + 'px';
});
msgFontEl.addEventListener('change', () => {
UI.applyAppearance(parseInt(scaleEl?.value || 100), parseInt(msgFontEl.value));
});
}
// Theme toggle buttons
document.querySelectorAll('#themeToggle .toggle-btn').forEach(btn => {
btn.addEventListener('click', () => {
const theme = btn.dataset.theme;
UI.applyTheme(theme);
UI.applyTheme(theme); // delegates to Theme.set() — single source of truth
document.querySelectorAll('#themeToggle .toggle-btn').forEach(b =>
b.classList.toggle('active', b === btn)
);
const p = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
p.theme = theme;
localStorage.setItem('cs-appearance', JSON.stringify(p));
});
});
@@ -224,10 +227,12 @@ Object.assign(UI, {
});
// System theme change listener (for "system" mode)
// Note: Theme.set('system') registers its own listener, but this
// additionally fires the theme.changed event for CM6 editors.
UI._systemThemeQuery = window.matchMedia('(prefers-color-scheme: dark)');
UI._systemThemeQuery.addEventListener('change', () => {
const p = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
if (p.theme === 'system') UI.applyTheme('system');
const mode = typeof Theme !== 'undefined' ? Theme.get() : 'system';
if (mode === 'system') UI.applyTheme('system');
});
},
@@ -236,17 +241,21 @@ Object.assign(UI, {
* @param {'light'|'dark'|'system'} mode
*/
applyTheme(mode) {
let resolved = mode;
if (mode === 'system') {
resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
if (resolved === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
// Delegate to Theme API which handles system-mode resolution and
// listens for OS preference changes.
if (typeof Theme !== 'undefined') {
Theme.set(mode);
} else {
document.documentElement.removeAttribute('data-theme');
// Fallback if Theme not loaded — resolve manually
let resolved = mode;
if (mode === 'system') {
resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.setAttribute('data-theme', resolved);
}
// Publish event for CM6 editors and extensions
if (typeof Events !== 'undefined' && Events.emit) {
const resolved = typeof Theme !== 'undefined' ? Theme.resolved() : mode;
Events.emit('theme.changed', { mode, resolved });
}
},