diff --git a/src/db/settings.rs b/src/db/settings.rs index 646aede..c7d21cb 100644 --- a/src/db/settings.rs +++ b/src/db/settings.rs @@ -206,6 +206,17 @@ impl Default for ActionSettings { } } +/// 配色:浅色 / 深色 / 跟随系统。 +/// 落到 `` 上,深色由组件库的令牌块接管,页面不用改组件。 +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "snake_case")] +pub enum ThemeSetting { + Light, + Dark, + #[default] + Auto, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(default)] pub struct SiteMetaSettings { @@ -213,6 +224,8 @@ pub struct SiteMetaSettings { pub title: String, /// favicon 地址;空字符串表示用内置图标 pub favicon: String, + /// 配色 + pub theme: ThemeSetting, pub brand: BrandSettings, pub actions: ActionSettings, } @@ -222,6 +235,7 @@ impl Default for SiteMetaSettings { Self { title: "Stash · 本地应用工作区".into(), favicon: String::new(), + theme: ThemeSetting::Auto, brand: BrandSettings::default(), actions: ActionSettings::default(), } diff --git a/stash-web/src/App.css b/stash-web/src/App.css index d6c5b62..d897780 100644 --- a/stash-web/src/App.css +++ b/stash-web/src/App.css @@ -34,6 +34,11 @@ background: linear-gradient(to bottom, transparent, var(--su-bg-page)); } +/* 这张背景图是浅色渐变图,深色下留着会糊成一层脏灰,直接不显示 */ +[data-scheme='dark'] .site-background { + display: none; +} + /* ---------- 页头 ---------- */ .site-header-inner { diff --git a/stash-web/src/AppNext.tsx b/stash-web/src/AppNext.tsx index 9740bb0..629b2e4 100644 --- a/stash-web/src/AppNext.tsx +++ b/stash-web/src/AppNext.tsx @@ -284,6 +284,7 @@ export default function AppNext() { site: { title: 'Stash · 本地应用工作区', favicon: '', + theme: 'auto', brand: { name: 'STASH', mark: 'S', logo: '' }, actions: { refresh: true, settings: true, session: true }, }, @@ -291,6 +292,11 @@ export default function AppNext() { const keywordLower = keyword.trim().toLowerCase() + // 配色:写在 上,深色由组件库的令牌块接管 + useEffect(() => { + document.documentElement.dataset.scheme = site.site.theme + }, [site.site.theme]) + // 站点级设置:浏览器标签标题与 favicon(空值回落到内置) useEffect(() => { const title = site.site.title.trim() diff --git a/stash-web/src/components/admin/AdminNext.tsx b/stash-web/src/components/admin/AdminNext.tsx index 3f4bfb1..6b43aa4 100644 --- a/stash-web/src/components/admin/AdminNext.tsx +++ b/stash-web/src/components/admin/AdminNext.tsx @@ -33,6 +33,7 @@ import { type SettingsDocument, type SettingsTone, type SiteSettings, + type SiteTheme, } from '../../settings' import { createCard, @@ -126,13 +127,14 @@ export function AdminNext() { void load() }, []) - // 后台壳也要吃站点级设置:保存后标签标题 / favicon 立刻变,否则会以为「设了没反应」 + // 后台壳也要吃站点级设置:保存后标签标题 / favicon / 配色立刻变,否则会以为「设了没反应」 // 注意:这里的 `document` 是设置文档,浏览器文档要用 `window.document` useEffect(() => { const meta = document?.settings.site if (meta === undefined) return const title = meta.title.trim() if (title !== '') window.document.title = title + window.document.documentElement.dataset.scheme = meta.theme const href = meta.favicon.trim() === '' ? '/favicon.svg' : meta.favicon.trim() let link = window.document.head.querySelector('link[rel="icon"]') if (link === null) { @@ -141,7 +143,7 @@ export function AdminNext() { window.document.head.appendChild(link) } link.href = href - }, [document?.settings.site.title, document?.settings.site.favicon]) + }, [document?.settings.site.title, document?.settings.site.favicon, document?.settings.site.theme]) const authenticated = session?.authenticated === true @@ -1762,6 +1764,12 @@ function CardTable({ const TONE_SELECT = TONE_OPTIONS.map((option) => ({ value: option.value, label: option.label })) +const THEME_SELECT = [ + { value: 'auto', label: '跟随系统' }, + { value: 'light', label: '浅色' }, + { value: 'dark', label: '深色' }, +] + function HeaderPanel({ value, onChange, @@ -1794,6 +1802,13 @@ function HeaderPanel({ onChange={(event) => onChangeSite({ ...site, favicon: event.target.value })} /> + +