This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/src/js/sw/surfaces/settings/appearance.js
Jeffrey Smith 4dd3fe622e
Some checks failed
CI/CD / test-frontend (pull_request) Has been cancelled
CI/CD / test-sqlite (pull_request) Has been cancelled
CI/CD / test-go-pg (pull_request) Has been cancelled
CI/CD / build-and-deploy (pull_request) Has been cancelled
CI/CD / detect-changes (pull_request) Has been cancelled
Feat v0.6.10 viewport foundation
Single layout model: body → shell → surface. CSS zoom replaces
transform: scale() — no more getBoundingClientRect hacks. Extension
surfaces use height: 100% instead of 100vh. All viewport-height
declarations use 100dvh fallbacks for mobile browsers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:28:35 +00:00

79 lines
2.8 KiB
JavaScript

/**
* AppearanceSection — theme, UI scale
*/
const { html } = window;
const { useState, useCallback } = hooks;
// Migrate cs- prefix to sb- (one-time, preserves existing prefs)
if (!localStorage.getItem('sb-appearance') && localStorage.getItem('cs-appearance')) {
localStorage.setItem('sb-appearance', localStorage.getItem('cs-appearance'));
localStorage.removeItem('cs-appearance');
}
const THEMES = [
{ key: 'light', icon: '\u2600', label: 'Light' },
{ key: 'dark', icon: '\u263E', label: 'Dark' },
{ key: 'system', icon: '\u2395', label: 'System' },
];
function getPrefs() {
try { return JSON.parse(localStorage.getItem('sb-appearance') || '{}'); }
catch { return {}; }
}
export function AppearanceSection() {
const prefs = getPrefs();
const [theme, setTheme] = useState(sw.theme?.current || 'system');
const [scale, setScale] = useState(prefs.scale || 100);
const applyTheme = useCallback((mode) => {
setTheme(mode);
sw.theme.set(mode);
sw.emit('theme.changed', { mode, resolved: sw.theme.current });
}, []);
const save = useCallback(() => {
const p = getPrefs();
p.scale = scale;
localStorage.setItem('sb-appearance', JSON.stringify(p));
// Apply scale to content area only (not shell/nav).
// CSS zoom reflows layout correctly — no getBoundingClientRect hacks needed.
const inner = document.getElementById('surfaceInner');
if (inner) {
inner.style.zoom = scale !== 100 ? (scale / 100) : '';
}
sw.emit('toast', { message: 'Appearance saved', variant: 'success' });
}, [scale]);
return html`
<div class="settings-section">
<h3>Theme</h3>
<div class="toggle-group" style="display:flex;gap:0;">
${THEMES.map(t => html`
<button class="toggle-btn ${theme === t.key ? 'active' : ''}"
onClick=${() => applyTheme(t.key)}>
${t.icon} ${t.label}
</button>
`)}
</div>
</div>
<div class="settings-section">
<h3>UI Scale</h3>
<div class="form-group">
<div style="display:flex;align-items:center;gap:8px;">
<input type="range" min="80" max="175" step="5"
value=${scale}
onInput=${e => setScale(parseInt(e.target.value))}
style="flex:1;" />
<span style="font-size:12px;color:var(--text-2);font-family:var(--mono);min-width:32px;">
${scale}%
</span>
</div>
</div>
</div>
<button class="btn-md btn-primary" onClick=${save}>Save</button>
`;
}