Skip to content

feat: 深色模式手动开关(当前只能跟随系统) #6

Description

@yujiezhang-ops

现状

深色模式的样式已经完整实现,但只能跟随系统,用户无法手动选择。

frontend/src/styles/tokens.css:root 定义浅色变量(:6-36),@media (prefers-color-scheme: dark) 覆盖颜色(:40-69)。全 app 没有任何 data-theme / .theme-dark class / classList 操作 —— 没有可供开关挂载的钩子。

frontend/index.html:7-8 的两个 <meta name="theme-color"> 同样是 media 驱动。

现成的参照实现

这个仓库的 site/ 已经做完了这件事,可以直接搬:

app 前端也有一套可复用的状态模式:i18n.tsx 的 Provider + localStorage + try/catch 兜底 + documentElement 副作用(i18n.tsx:321-323 同步 lang 属性,正是主题该挂的位置)。

方案

1. CSS 加钩子

tokens.css:40-69 的暗色变量块提取出来,让它同时被媒体查询和显式 class 命中。三态:跟随系统 / 强制浅色 / 强制深色。

注意一个细节:暗色块目前只覆盖颜色变量--radius-*--sidebar-width--footer-height 只在浅色 :root 定义一次。提取时不要把这些尺寸变量一起搬进去,否则强制浅色时它们会丢失。

2. ThemeProvider

i18n.tsx 的结构写,复用它的 try/catch 降级(注释说明 hardened webview 里 storage 可能不可用,此时退化为内存态)。

localStorage key 需要定一下命名:site 用 oneagent-theme(连字符),app 的 i18n 用 oneagent.locale(点号)。建议 app 内统一用点号风格 oneagent.theme,与同一个 storage 命名空间下的 locale 保持一致,不必迁就 site(两者不共享 storage)。

3. 防闪烁

Wails app 加载本地资源,闪烁窗口比网页短,但仍需处理:在 index.html 里加 inline script 在 React 挂载前就设好 class。同时 <meta name="theme-color"> 需要跟着手动选择更新,不能只靠 media。

4. 开关放哪

侧边栏底部已经有语言选择器和任务中心,主题开关放在语言选择器旁边最自然 —— 两者都是「应用外观/偏好」类设置。

验收

  • 三态可切换,重启应用后保持
  • 未做过显式选择时跟随系统,且系统切换时实时响应
  • 显式选择后不再被系统偏好覆盖
  • 首屏无闪烁
  • 强制浅色时尺寸类变量不丢失
  • storage 不可用时降级为内存态而非崩溃
  • 覆盖测试:三态切换、持久化、系统偏好仅在无显式值时生效

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions