setup
This commit is contained in:
@@ -194,6 +194,10 @@ a {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.app-header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
@@ -591,3 +595,98 @@ a {
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.setup-shell {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.setup-title {
|
||||
margin: 0.75rem 0 0;
|
||||
}
|
||||
|
||||
.setup-notice {
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--accent-2);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--surface) 94%, transparent);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.setup-form {
|
||||
margin-top: 1rem;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.setup-panel,
|
||||
.setup-db-panel {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--surface) 92%, transparent);
|
||||
padding: 16px;
|
||||
box-shadow: 0 10px 24px rgba(1, 22, 32, 0.06);
|
||||
}
|
||||
|
||||
.setup-panel--flat {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.setup-db-panel {
|
||||
scroll-margin-top: 18px;
|
||||
}
|
||||
|
||||
.setup-panel__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.setup-panel__head h2,
|
||||
.setup-panel__head h3 {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
.setup-panel__head p {
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
|
||||
.setup-tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.setup-db-panels {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.setup-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.setup-field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.setup-field > span {
|
||||
font-weight: 800;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.setup-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@@ -8,38 +8,75 @@ function readThemePreference(key, fallback) {
|
||||
}
|
||||
}
|
||||
|
||||
const themeMode = readThemePreference('nexus.theme', document.documentElement.dataset.theme || 'day');
|
||||
const themeAccent = readThemePreference('nexus.accent', document.documentElement.dataset.accent || 'logo');
|
||||
const moduleName = document.documentElement.dataset.module || '';
|
||||
const mainThemeMode = readThemePreference('nexus.theme', 'day');
|
||||
const mainThemeAccent = readThemePreference('nexus.accent', 'logo');
|
||||
const moduleThemeMode = moduleName ? readThemePreference(`nexus.module.${moduleName}.theme`, 'inherit') : mainThemeMode;
|
||||
const moduleThemeAccent = moduleName ? readThemePreference(`nexus.module.${moduleName}.accent`, 'inherit') : mainThemeAccent;
|
||||
|
||||
function applyTheme(mode, accent) {
|
||||
const normalizedMode = ['day', 'night'].includes(mode) ? mode : 'day';
|
||||
const normalizedAccent = ['logo', 'pink', 'cyan', 'orange', 'green'].includes(accent) ? accent : 'logo';
|
||||
document.documentElement.dataset.theme = normalizedMode;
|
||||
document.documentElement.dataset.accent = normalizedAccent;
|
||||
function normalizeThemeValue(value, allowed, fallback) {
|
||||
return allowed.includes(value) ? value : fallback;
|
||||
}
|
||||
|
||||
function storedTheme() {
|
||||
const rawMode = moduleName ? readThemePreference(`nexus.module.${moduleName}.theme`, 'inherit') : readThemePreference('nexus.theme', 'day');
|
||||
const rawAccent = moduleName ? readThemePreference(`nexus.module.${moduleName}.accent`, 'inherit') : readThemePreference('nexus.accent', 'logo');
|
||||
const mode = moduleName ? normalizeThemeValue(rawMode, ['inherit', 'day', 'night'], 'inherit') : normalizeThemeValue(rawMode, ['day', 'night'], 'day');
|
||||
const accent = moduleName ? normalizeThemeValue(rawAccent, ['inherit', 'logo', 'pink', 'cyan', 'orange', 'green'], 'inherit') : normalizeThemeValue(rawAccent, ['logo', 'pink', 'cyan', 'orange', 'green'], 'logo');
|
||||
return { mode, accent };
|
||||
}
|
||||
|
||||
function applyTheme(mode, accent, persist = true) {
|
||||
const allowedModes = moduleName ? ['inherit', 'day', 'night'] : ['day', 'night'];
|
||||
const allowedAccents = moduleName ? ['inherit', 'logo', 'pink', 'cyan', 'orange', 'green'] : ['logo', 'pink', 'cyan', 'orange', 'green'];
|
||||
const normalizedMode = normalizeThemeValue(mode, allowedModes, moduleName ? 'inherit' : 'day');
|
||||
const normalizedAccent = normalizeThemeValue(accent, allowedAccents, moduleName ? 'inherit' : 'logo');
|
||||
const effectiveMode = normalizedMode === 'inherit' ? mainThemeMode : normalizedMode;
|
||||
const effectiveAccent = normalizedAccent === 'inherit' ? mainThemeAccent : normalizedAccent;
|
||||
document.documentElement.dataset.theme = effectiveMode;
|
||||
document.documentElement.dataset.accent = effectiveAccent;
|
||||
try {
|
||||
localStorage.setItem('nexus.theme', normalizedMode);
|
||||
localStorage.setItem('nexus.accent', normalizedAccent);
|
||||
if (persist) {
|
||||
if (moduleName) {
|
||||
if (normalizedMode === 'inherit') {
|
||||
localStorage.removeItem(`nexus.module.${moduleName}.theme`);
|
||||
} else {
|
||||
localStorage.setItem(`nexus.module.${moduleName}.theme`, normalizedMode);
|
||||
}
|
||||
if (normalizedAccent === 'inherit') {
|
||||
localStorage.removeItem(`nexus.module.${moduleName}.accent`);
|
||||
} else {
|
||||
localStorage.setItem(`nexus.module.${moduleName}.accent`, normalizedAccent);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem('nexus.theme', normalizedMode);
|
||||
localStorage.setItem('nexus.accent', normalizedAccent);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore blocked storage; the current page still receives the theme.
|
||||
}
|
||||
return { mode: normalizedMode, accent: normalizedAccent };
|
||||
}
|
||||
|
||||
applyTheme(themeMode, themeAccent);
|
||||
const initialTheme = applyTheme(moduleThemeMode, moduleThemeAccent, false);
|
||||
|
||||
const themeModeSelect = document.querySelector('[data-theme-mode]');
|
||||
const themeAccentSelect = document.querySelector('[data-theme-accent]');
|
||||
|
||||
if (themeModeSelect) {
|
||||
themeModeSelect.value = document.documentElement.dataset.theme;
|
||||
themeModeSelect.value = initialTheme.mode;
|
||||
themeModeSelect.addEventListener('change', () => {
|
||||
applyTheme(themeModeSelect.value, document.documentElement.dataset.accent);
|
||||
const current = storedTheme();
|
||||
applyTheme(themeModeSelect.value, current.accent);
|
||||
});
|
||||
}
|
||||
|
||||
if (themeAccentSelect) {
|
||||
themeAccentSelect.value = document.documentElement.dataset.accent;
|
||||
themeAccentSelect.value = initialTheme.accent;
|
||||
themeAccentSelect.addEventListener('change', () => {
|
||||
applyTheme(document.documentElement.dataset.theme, themeAccentSelect.value);
|
||||
const current = storedTheme();
|
||||
applyTheme(current.mode, themeAccentSelect.value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user