Files
SnowLuma/packages/webui/index.html
T
DrSmoothl 44a29c6ee4 feat(webui): OLED/accent/palette theming fixes + light-dark reveal
- OLED: toggle switch off-track is now a neutral foreground tint with a white
  knob (was invisible bg-muted + bg-background on pure black); the OLED sidebar
  override is scoped to sidebar-style=follow so panel/accent work in OLED too.
- Accent: the chosen accent now also tints the selected-state surfaces
  (--accent / --sidebar-accent) so the sidebar active item and settings nav
  rail follow it — default palette only, respecting accent scope.
- Palettes: add 6 more soft schemes (Rosé Pine / Moon / Dawn, Nord, Everforest
  dark + light) via a pal() builder; schema unions + normalizer + bootstrap
  light/dark classification updated to match.
- Light/dark switch now animates: a circular View Transitions reveal from the
  click (mode/palette/intensity changes only; gated off under reduced/disabled
  motion; instant fallback where the API is absent).
2026-06-14 16:20:05 +08:00

61 lines
3.5 KiB
HTML

<!doctype html>
<html lang="zh-CN" class="light" data-theme="light">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="theme-color" content="#0ea5e9" />
<!-- WebUI is an admin surface; tell every compliant crawler to skip it. -->
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
<meta name="googlebot" content="noindex, nofollow, noarchive, nosnippet" />
<meta name="bingbot" content="noindex, nofollow, noarchive, nosnippet" />
<title>SnowLuma 控制台</title>
<!-- Pre-paint theme bootstrap: applies the cached appearance to <html>
before the bundle mounts, so dark/OLED/density/scale don't flash from
the default light theme. Mirrors a subset of ThemeContext.applyAppearance;
defensive (never breaks page load). The bundle re-applies authoritatively. -->
<script>
(function () {
try {
var raw = localStorage.getItem('snowluma_ui_appearance');
if (!raw) return;
var a = JSON.parse(raw);
if (!a || typeof a !== 'object') return;
var root = document.documentElement;
var mode = a.mode;
var pal = a.palette;
// A non-default palette fixes light/dark; these flavors are light, the rest dark.
var LIGHT_PAL = { 'catppuccin-latte': 1, 'rose-pine-dawn': 1, 'everforest-light': 1 };
var isPal = typeof pal === 'string' && pal.length > 0 && pal !== 'default';
var resolved = isPal
? (LIGHT_PAL[pal] ? 'light' : 'dark')
: (mode === 'light' || mode === 'dark'
? mode
: (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'));
root.classList.remove('light', 'dark');
root.classList.add(resolved);
root.setAttribute('data-theme', resolved);
root.style.colorScheme = resolved;
if (isPal) root.setAttribute('data-palette', pal);
if (a.density === 'compact' || a.density === 'cozy') root.setAttribute('data-density', a.density);
// A palette forces soft so the OLED "black" rule can't fight it.
var intensity = isPal ? 'soft' : a.darkIntensity;
if (intensity === 'black' || intensity === 'soft') root.setAttribute('data-dark-intensity', intensity);
if (a.sidebarStyle === 'follow' || a.sidebarStyle === 'panel' || a.sidebarStyle === 'accent') root.setAttribute('data-sidebar-style', a.sidebarStyle);
root.setAttribute('data-contrast', a.highContrast ? 'high' : 'normal');
root.setAttribute('data-reduce-motion', a.reduceMotion ? '1' : '0');
if (typeof a.radius === 'number' && a.radius >= 0 && a.radius <= 2) root.style.setProperty('--radius', a.radius + 'rem');
if (typeof a.uiScale === 'number' && a.uiScale >= 0.5 && a.uiScale <= 2) root.style.fontSize = 16 * a.uiScale + 'px';
} catch (e) { /* ignore — fall back to default theme */ }
})();
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&family=Noto+Sans+SC:wght@400;500;600;700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>