Skip to content

dsh-seasons-theme

Verified

dsh-seasons-theme · v1.0.2 · MIT · Web UI

四季主题:为 DSH Web GUI 增加默认与春、夏、秋、冬四套季节主题,每套均含浅色/深色,并提供侧栏主题切换菜单。

Install

dsh plugin add dsh-seasons-theme

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

四季主题 · dsh-seasons-theme

给 DSH Web GUI 增加 默认 + 春 / 夏 / 秋 / 冬 五套主题,每套都有浅色 / 深色两种模式;侧栏底部(设置按钮上方)新增一个主题切换菜单,四时色卡与明暗开关都在里面。

配色并非另配一套,而是直接沿用原教学系统的四季调色板(web/css/themes.css):

季节 主题名 浅色纸面 深色纸面
默认 暖纸 #f5f6f3 #17212e
春 花信 #f4f7f1 #19252e
夏 晴海 #f0f7f7 #152531
秋 知秋 #f8f5ef #232832
冬 初雪 #f2f5f9 #1a2435

做了什么

  • 季节 = 覆盖层:通过 DSH 的主题扩展点 ctx.theme.overrideTokens(),把一套季节 token(浅/深成对)叠在内置主题上。季节选择记在浏览器 localStorage;「默认」不注册覆盖层,保持 DSH 原生外观。
  • 明暗 = DSH 自带偏好:浅色/深色按钮调用 ctx.theme.setTheme('light' | 'dark'),因此与「设置 → 通用 → 外观」完全同步。偏好为 跟随系统 且系统此刻正好是所点模式时不会改写偏好,系统切换深浅时菜单状态也会跟着变。
  • 切换菜单:触发按钮注册在 sidebar.footer.action(展开时是「主题 + 当前季节」,收起时是 36×36 图标);浮层注册在 shell.overlay,从触发按钮处向上展开,点击外部 / Esc 关闭,Tab 有焦点循环。
  • 弹层为不透明纸面:主题菜单与其他弹出式菜单统一使用不透明的季节纸面底色,不做毛玻璃、不做背景模糊、没有任何发光描边;文字层级与可点击区域保持宿主原有行为。
  • 菜单展开/收起:主题切换菜单展开与收起是一对互逆的纯淡入淡出(约 140–160ms,无位移、无缩放、无过冲甩尾),收起动画播完才卸载。
  • 四季插画背景:四季各有原教学系统原版 720×240 插画(web/assets/themes/*.svg,构建时内联)。按原教学系统的处理方式只放在外壳边缘——页面右下角一幅、侧栏底部一幅(展开时才有),低透明度、顶部渐隐,内容区不加图案、不改变卡片底色;默认主题不铺插画,保持原生外观。手机上侧栏只剩 56px 导轨,侧栏插画自动隐藏,只留页面右下角一幅(更淡);侧栏展开/收起时插画跟着做缩放淡入淡出,和侧栏动画同步。
    • 说明:DSH 没有开放"页面背景"槽位,插画是以 pointer-events:none 的边缘覆盖层画在 shell.overlay 里的,因此不写宿主 DOM、不遮点击;文字密集处它自然很淡。
  • 太阳 / 月亮:浅色模式在插画的日轮位置叠一枚季节色太阳(圆盘 + 八道光芒 + 柔光);深色模式叠一枚原教学系统同款的弯月(同一套 night-sky-moon 月牙路径 + 暖金柔光)。手机端太阳/月亮移到右上角,避免压到输入框。
  • 深色星空与流星:季节深色模式下叠加 stars.svg 星点底纹 + 16 颗自绘闪烁星;每 8–15 秒随机落下 1–2 颗流星,位置、长度、时长都随机;同一批的两颗像同一场流星雨——飞行方向一致,入射角差 ≤6°。prefers-reduced-motion 下流星与闪烁关闭,插画保留。
  • 运行中标签的扫光动画:深度求索中,用时… 与上方运行行使用 DSH 的 TextShimmer。之前把 label-deep-diving / label-deep-diving-shimmer 映射成了半透明色,扫光块变成透明→动画看不见;现在两个 token 都是不透明的季节色(深色下底色为中调、扫光为亮调),扫光恢复可见。
  • 夜间鼠标光晕(默认关闭):在「设置 → 通用 → 鼠标光晕拖尾」里打开后,季节深色模式下鼠标在操作区移动时跟一层很淡的季节色光晕(直径约 60px)+ 3 个渐小的拖尾点;指针静止约 0.7 秒自动淡出并停止 rAF。开关按浏览器记忆(localStorage),默认关闭;侧栏、主题菜单内、触屏设备不显示;pointer-events:none 且 mix-blend-mode:screen,不拦截任何点击/拖拽,prefers-reduced-motion 下关闭。
  • 切换过渡:季节切换与明暗切换都有 420ms 淡入过渡——支持 View Transitions 的浏览器做整页交叉淡入(新画面盖在旧画面上淡入,不闪白、不打断菜单操作);不支持的浏览器退化为背景/文字/边框/图标描边的 420ms 颜色渐变。prefers-reduced-motion 下直接切换。
  • 覆盖范围:110 个 token(--dsw-alias-*、--dsw-specific-*、菜单底色等),覆盖画布、卡片、浮层、侧栏、菜单、按钮、输入、Markdown 代码块、文件 diff、状态色、滚动条、Toast、Tooltip、引导页等。遮罩、图片工具栏、shiki 语法色、阴影刻意保留原生值。
  • 兼容与可验证:所有派生色在构建时算成普通 #rrggbb / rgba(),生成物里没有 color-mix(),较老的手机浏览器也能正确上色。build.mjs 会在构建时用真实 token 做对比度/亮度断言,写反一处就直接构建失败。

构建时强制保证

node build.mjs 会对四套季节 × 明暗两种模式逐项检查:

  • 正文 label-primary 在画布、卡片、次级表面、侧栏、气泡、菜单材质、代码块等 13 类内容表面上 ≥ 4.5:1;
  • 次级文字在主要表面上 ≥ 4.5:1,三级文字在纸面与菜单材质上 ≥ 4.5:1;
  • 主按钮文字、Toast 文字、浮层角标文字、链接、状态色文字 ≥ 4.5:1;运行中标签底色 ≥4.5:1,扫光色与底色文字拉开可感知差异;
  • 发送键(写死白图标)的填充色与白色 ≥ 3.5:1;
  • 深色模式下所有“浅色表面类” token 亮度 ≤ 0.45,浅色模式下的纸面类 token 亮度 ≥ 0.5(防止深浅写反);
  • 深色代码块必须比它的标题条更暗,也必须比卡片更暗;深色细描边必须弱于 l2 描边;
  • 所有 token 值只允许 #hex / rgba() / var() / linear-gradient()。

安装

从 npm(推荐)

# 在 DSH 会话中调用 plugin_manager
action: install_bundle
target: [email protected]

或命令行:

dsh plugin --profile web add dsh-seasons-theme

从 GitHub

dsh plugin --profile web add github:Sirilean/dsh-seasons-theme

从“发现”页(自定义市场索引)

  1. 插件页 →「实验性插件 → 更多」启用自定义插件市场;
  2. 「发现 → 添加索引源」,填: https://sirilean.github.io/dsh-seasons-theme/market.json
  3. 搜到「四季主题」后点安装。

这个地址需要先在 GitHub 仓库 Settings → Pages 里把 main 分支根目录设为 Pages;没开 Pages 时前两种方式照样可用。

本地开发

action: install_bundle
target: D:\工作区\dsh-seasons-theme

或:

dsh plugin --profile web add link:D:/工作区/dsh-seasons-theme

安装后侧栏底部「设置」上方会出现「主题」一行,点开即是四时菜单。已打开的页面通常会在几秒内通过 client HMR 自动加载新 bundle;若未生效(例如没有运行 bundle watcher),刷新页面即可。 卸载/停用后覆盖层、样式与菜单会一起移除,不会留下写死的颜色。

目录

dsh-seasons-theme/
├─ package.json          # bundle 清单 + dsh.client(web / immediately / inject)
├─ cordis.patch.yml      # 插入 Host 行
├─ index.js              # Host 空壳
├─ client.js             # 构建产物:调色板数据 + 主题覆盖 + 菜单(自包含,无外部依赖)
├─ build.mjs             # 由 src/ 生成 client.js,并做对比度/亮度/兼容性断言
├─ market.json           # 自建插件市场索引(format 1,GitHub Pages 托管)
├─ .github/workflows/    # verify(push/PR)+ publish(打 tag 时 npm publish --provenance)
├─ src/
│  ├─ palette.mjs        # 原教学系统四季 × 明暗调色板(唯一颜色出处)
│  ├─ tokens.mjs         # 调色板 → DSH token 的映射规则(构建时算色)
│  └─ client.template.js # 菜单、插画层、星空/流星与插件逻辑模板
├─ assets/               # 原教学系统四季插画与星点图(构建时内联为 data URI)
├─ tests/smoke.mjs       # Node 冒烟测试:注册、菜单、覆盖层、明暗、对比度、插画、卸载
├─ locale/{zh,en}.json   # 插件列表里的标题与说明
└─ icon.svg              # 四时主题图标

改配色只需要动 src/palette.mjs(或 src/tokens.mjs 里某个 token 的取法),然后:

node build.mjs        # 生成 client.js + 校验
node --check client.js
node tests/smoke.mjs  # 冒烟测试

开源与发布

发布新版本:

  1. 修改 package.json 的 version;
  2. 在 market.json 的 versions 数组里追加一条(精确 SemVer + Unix 秒 publishTimestamp);
  3. 提交并打 tag:git tag vX.Y.Z && git push origin vX.Y.Z;
  4. GitHub Actions 的 publish 工作流会用 NPM_TOKEN secret 执行 npm publish --provenance(prepublishOnly 会先跑 npm run verify);
  5. 用户在「发现」页刷新(缓存约 10 分钟)即可看到新版本;当前市场不支持原地升级,需要卸载后重装。

也可以本地手动发布:

npm run verify
npm pack --dry-run   # 检查清单
npm publish

更新

  • 1.0.2:统一主题菜单面板——默认主题(尤其浅色)下不再使用宿主的半透明菜单底色,改为不透明 bg-layer-1,与四季主题下的菜单一致。
  • 1.0.1:修复侧栏底部插画在拖动侧栏宽度时不跟随、裁切错位的问题。
  • 已安装旧版的人:先在插件页看 dsh-seasons-theme 是否出现「可更新 / 更新」并点更新;如果只有「已安装」,就卸载后重新安装:
    1. 插件 → 已安装插件 → dsh-seasons-theme → 卸载;
    2. 插件 → 添加插件 → 输入 dsh-seasons-theme(或精确指定 [email protected]);
    3. 刷新页面。
  • 也可以用 plugin_manager:remove_bundle(target dsh-seasons-theme)→ install_bundle(target [email protected])。
  • 季节、明暗、鼠标光晕开关都存在当前浏览器的 localStorage,卸载重装不会丢失;主题菜单里的当前季节会自动恢复。
  • 市场索引有约 10 分钟缓存;刚发的版本若暂时看不到,等缓存过期后刷新「发现」页。

兼容性

  • 在 DSH 0.2.1-rc.2 上测试通过;插件只依赖 DSH 内置 Client 包,DSH 升级后若包名或扩展点变化,需要跟着更新。
  • 只用公开扩展点:ctx.theme(getTheme / setTheme / overrideTokens / theme/change)、ctx.slots、ctx.locale;没有 import 任何 Harness Client 包,也没有读写别的插件的 DOM。
  • 依赖的内置包:@deepseek-ai/dsh-client-ui-theme(主题服务)、dsh-client-ui-sidebar(sidebar.footer.action)、dsh-client-ui-layout(shell.overlay)。缺少时插件不激活,不会报错刷屏。
  • 与内置「外观」设置共存:季节是一层,明暗是内置偏好,两者互不覆盖。

已知限制

  • 季节选择存在当前浏览器的 localStorage,不跨浏览器同步(与原教学系统原版一致)。
  • 主题 token 是 DSH 的 alias 层;若未来 DSH 新增组件只消费静态色(--dsw-static-*),那部分不会跟着换季。这一类颜色属于原生语义色,本插件有意不动。
  • 少数组件把白字直接写进样式(例如附件失败徽标用 state-error-primary 作底 + 固定白字)。深色季节的粉色错误色与白字对比约 2:1;发送键同 token 也被 11px 徽标文字复用(深色约 2.3–2.5:1,原生约 5.2:1)。要在“白图标可用”和“徽标文字可读”之间取舍;如更在意徽标文字,可把 button-info-fill 深色比例从 55% 调到 65%。
  • 视觉验证:本次交付在无浏览器控制的环境下完成,已确认 JS 语法、manifest、安装结果、插槽的实时注册、HMR 实际加载新 bundle、主题服务接受 110 个覆盖 token、构建期对比度/亮度断言与冒烟测试全部通过;像素级观感请刷新页面后目视确认。