Skip to main content
Y

dsh-custom-style

yutao8/dsh-custom-style

Adds a Custom CSS section to Settings for adjusting dialog dimensions, font sizes, full-window wallpapers, and inspecting stable selectors.

Install

dsh plugin --profile web add github:yutao8/dsh-custom-style

README

dsh-custom-style

English | 中文

把你自己写的 CSS 注入 DeepSeek Harness 界面——设置弹窗宽度、字号、配色、间距,想改什么就改什么。 只需编辑 $DSH_HOME/dsh-custom-style/ 下的 *.css 文件,保存约 2 秒后自动生效:不改应用本体、不重新打包、不重启。

它还会在 DSH 设置界面里加一栏「自定义样式」:用几个常用数值一键调整弹窗宽度、高度、正文字号与内容居中,或是设置一张带磨砂遮罩的全屏背景图——不用离开应用就能调界面。

效果预览

  • 宿主半身(index.js):把 $DSH_HOME/dsh-custom-style/*.css 按文件名顺序拼成一张样式表,经 GET /dsh-custom-style/all.css 提供;首次运行自动创建目录并播种起始样式表,绝不覆盖已有文件。 另外为设置面板提供文件接口(/files、/file、/file/delete、/reveal)。
  • 浏览器半身(client.js):把样式表注入自己持有的 <style data-plugin-css="dsh-custom-style">, 每 2 秒重新拉取一次(页面隐藏时暂停),所以改完保存就会生效,无需刷新;设置分区通过客户端 slot API 注册。
  • 零第三方运行时依赖:宿主半身只用 node: 内置模块,浏览器半身只用 DOM、fetch 和平台提供的 React。 因此 harness 升级不会破坏它;用户 CSS 存在包外,重装插件也不会丢样式。

目录

环境要求

  • 带 web/桌面客户端的 DeepSeek Harness(dsh 0.2.x),已在 0.2.0-rc.2 上验证。
  • Node.js ^22.19.0 || >=24.0.0(只有可选的本地安装器与测试套件需要)。

安装

A. 从插件目录 / npm 安装(本仓库被收录之后):

dsh plugin add dsh-custom-style

没有构建步骤——client.js 是手写的、不是打包产物——所以直接从本仓库源码安装也可以。

B. 从本仓库安装到当前这台机器(仓库自带安装器做的事):

node install.mjs            # 只预览(dry run)
node install.mjs --apply    # 安装到 $DSH_HOME/profiles/desktop

它会幂等地把插件登记进 DSH profile:

位置变成什么
<profile>/local-plugins/dsh-custom-style目录联接 → 本仓库(单一源码);--copy 则改为复制文件
<profile>/node_modules/dsh-custom-style目录联接 → 上面那一层
<profile>/package.jsondependencies["dsh-custom-style"] = "link:./local-plugins/dsh-custom-style" 加一条 dsh.profile.bundles 登记(改前备份)
$DSH_HOME/dsh-custom-style/00-starter.css你的样式表——仅在缺失时写入

浏览器版 DSH 用 --profile web;非默认 DSH_HOME 用 --dsh-home <路径>。 安装后完全退出并重开 DSH 一次(宿主半身在启动时注册路由);之后改 CSS 再也不需要重启。

使用

打开 设置 → 自定义样式。

调节:宽视野 (1500px)调节:紧凑 (1000px)
宽视野预设紧凑预设
调节:默认尺寸 (800px)背景:设置全屏壁纸与遮罩
默认尺寸换背景图

面板分为「调节」、「背景」与「选择器与目录」三页:

顶部 tab能做什么
调节八个数字 + 六组预设(宽视野 / 紧凑 / 纵向扩展 / 小巧 / 最大化 / 恢复默认)。预设按钮在输入项上方,先套用再微调;点「应用」写入 20-quick-settings.css
背景图片地址(填网址,或点「选择图片」从本机挑)/ 遮罩浓度 / 背景模糊 / 平铺方式;带缩略图预览与「清除背景图」
选择器与目录显示当前 CSS 样式文件存放路径与「打开目录」按钮;提供从 deepseek-harness 源码核对过的稳定选择器举例与一键「复制」代码片段

三个 tab 各一页,一次只显示一页。较长的解释收在「说明」折叠里,默认收起。

习惯直接改文件也行:编辑 %DSH_HOME%\dsh-custom-style\ (默认 C:\Users\<你>\.dsh\dsh-custom-style\)下的任意 *.css——面板和文件夹改的是同一批文件:

/* 01-my-tweaks.css —— 保存后约 2 秒生效 */
[data-shortcut-modal="settings"] { width: 1200px !important; }    /* 设置弹窗宽度(默认 800px) */
[data-shortcut-modal="settings"] nav { width: 220px !important; } /* 左侧导航宽度(默认 188px) */
body { --dsh-content-font-size: 15px !important; }                /* 会话正文字号 */
  • 文件名决定拼接顺序(00- 在前、99- 在后),同特异性下后者胜出。
  • 只有在与官方规则特异性相同时才需要 !important(弹窗宽度就是这种情况)。
  • 新建、改名、删除文件都会在下一轮询(≤2 秒)自动反映。
  • 想把样式放在别处:设 DSH_CUSTOM_STYLE_DIR 为绝对路径,宿主半身会改读那个目录。

稳定选择器

官方类名是构建期哈希(今天是 .wCInkW_panel,升级后就变),所以优先用能活下来的钩子。 下面这张表可以直接抄进你的 CSS。

目标稳定选择器示例
设置弹窗(尺寸、圆角、背景)[data-shortcut-modal="settings"]width: 1200px !important;
它的左侧导航[data-shortcut-modal="settings"] navwidth: 220px !important;
导航里的单个条目[data-shortcut-modal="settings"] nav buttonheight: 44px !important;
当前所在的那一条导航[data-shortcut-modal="settings"] nav button[aria-current]background: … !important;
右侧内容列 / 滚动区[data-shortcut-modal="settings"] > div:last-childpadding-inline-start: 4px !important;
任意模态(兜底)div[role="dialog"][aria-modal="true"]border-radius: 14px !important;
设置页的错误提示[role="alert"]font-size: 12px !important;
正文字号body 上的 --dsh-content-font-size15px !important;
低一档字号(表格、摘要)body 上的 --dsh-content-font-size-secondary13px !important;
颜色、圆角、描边、阴影、焦点环body 上的 --dsw-* 令牌--dsw-radius-panel: 14px !important;
深色模式画布body[data-ds-dark-theme]background: #0f1114 !important;
指针输入(焦点环被置透明)html[data-input-modality="pointer"]恢复焦点环颜色
macOS 桌面端html[data-platform="darwin"]border-radius: 12px !important;
滚动条(官方令牌)body 上的 --dsh-scrollbar-width 等10px !important;
滚动条(WebKit 伪元素)::-webkit-scrollbarwidth: 10px;
代码块与行内代码pre, code, kbd, sampfont-size: 12.5px !important;
内置插件页标签栏[role="tablist"] / [role="tab"][data-active]gap: 18px !important;
设置页输入框 / 校验失败… input / … input[aria-invalid]border-radius: 8px !important;
插件市场里展开的卡片[data-open="true"]border-color: … !important;
侧栏会话行 / 工作区分组[data-row-key^="session:"] / ^="workspace:"border-radius: 8px !important;
侧栏账户浮层菜单[role="menu"] / [role="menuitem"]border-radius: 12px !important;
本插件注入的样式表style[data-plugin-css="dsh-custom-style"]在 DevTools 里定位自己的规则

完整清单(每条都标了它在出厂 bundle 里的核对出处)见 selectors.json。

要找别的元素:DevTools 里选中它 → Copy selector,优先挑由 data-*、aria-*、role 组成的写法, 而不是由哈希类名组成的。

两条已核实不再成立的选择器:旧版文档写过的 [data-shortcut-modal="dialog"] 与 [data-shortcut-modal="shortcut-edit"],在 0.2.0-rc.2 的出厂 bundle 里都不存在—— 整棵树上 data-shortcut-modal 只有 "settings" 这一个值。要命中别的模态浮层, 请用 div[role="dialog"][aria-modal="true"]。

弹窗自带 max-width: calc(100vw - 48px),所以设得再宽也不会溢出窄窗口。反过来说:窗口比 你的设定值 + 48px 更窄时,弹窗其实已经顶到上限——要么把 DSH 窗口拉大,要么自己缩小这条留白 (面板里的「弹窗两侧留白」就是干这个的):

[data-shortcut-modal="settings"] { max-width: calc(100vw - 16px) !important; }

为什么有些页面右侧还是留白

弹窗宽度只是一半原因。外壳的内容列本身不限宽,但各个页面会限制自己内容的宽度, 留白正是这个上限造成的。来自出厂 bundle 的实测:

页面被限宽的元素上限出处
内置插件.amjGKq_section760pxui-settings-plugins/lib/client.js:11
插件市场 tab.RotMhW_section760pxui-settings-plugin-inventory/lib/client.js:57
模型._3nPmjq_section720pxui-settings-models/lib/client.js:58
通用设置.zZnCHW_section不限宽ui-settings-general/lib/client.js:618
Agent 循环 / Shell无自带样式不限宽这两个包没有 CSS

(第三方插件另算:它们常用 _root / _shell / _wrap 之类的容器名把自己卡窄, 所以清单里一并按后缀命中。)

两个事实决定了修法。第一,外壳结构(来自出厂的 ui-settings-general 源码):

div[data-shortcut-modal="settings"]          role=dialog, aria-modal=true
├─ nav                                      188px
│  ├─ div(标题行,「设置」两个字)
│  └─ div(列表)
│     └─ button*(每个分区一条,选中的带 aria-current)
└─ div(内容列)
   ├─ div(顶部动作行:打开配置文件 + 关闭)
   └─ div(滚动区)
      └─ …                                  slot 渲染层,然后才是页面自己的容器

(结构出自 ui-settings-general/lib/client.js:280-342。)

滚动区是普通块级盒子,所以给被限宽的容器加 margin-inline: auto 就能把剩余空间均分—— 内容居中而不是贴左——而且对没有限宽的元素这是空操作。第二,被限宽的元素不是滚动区的 直接子节点(中间隔着 slot 层),而且它的类名是构建期哈希,所以要按稳定的后缀匹配:官方页是 _section,第三方页常见 _root / _shell / _wrap。面板里的「内容居中宽度」写的就是这个: 0 表示保留各页自己的宽度、只做居中;填数字表示把它们统一成该宽度并居中。

[data-shortcut-modal="settings"] [class*="_options"] > *,
[data-shortcut-modal="settings"] [class*="_section"],
[data-shortcut-modal="settings"] [class*="_root"],
[data-shortcut-modal="settings"] [class*="_shell"],
[data-shortcut-modal="settings"] [class*="_wrap"],
[data-shortcut-modal="settings"] [style*="max-width"] { margin-inline: auto !important; }

如果你希望内容铺满而不是居中,对同一组选择器再加 max-width: none。 它会把按窄列设计的页面拉伸(卡片网格摊开、文字行长变大),所以不作为默认。

工作原理

$DSH_HOME/dsh-custom-style/*.css
        │  按文件名顺序拼接,每个文件前加一条 "/* ===== 名字 ===== */" 横幅
        ▼
index.js(宿主半身)──GET /dsh-custom-style/all.css──▶  client.js(浏览器半身)
        │                                                    │
        └─ 自动创建目录、播种 00-starter.css                   └─ <style data-plugin-css="dsh-custom-style">,每 2 秒轮询
  • 路由面:GET /dsh-custom-style/all.css(拼接后的 CSS,Cache-Control: no-store)、 GET /dsh-custom-style/status 与 /files(JSON:目录、文件列表、字节数)、 POST /dsh-custom-style/file 与 /file/delete(写入/删除一个 *.css)、 POST /dsh-custom-style/reveal(打开目录);支持 HEAD,其余返回 404/405/413。
  • 只读写那个目录直接内部的 *.css;文件名必须匹配 [A-Za-z0-9][A-Za-z0-9._-]{0,63}\.css—— 不接受调用方传入的路径,因此没有路径穿越面。写入是原子的(临时文件 + rename),单文件上限 256 KiB。
  • 插件被卸载时,浏览器半身会移除自己的 <style> 并清掉定时器;设置分区来自客户端 slot API (settings.section),所以它的边框、导航与生命周期都由 harness 掌管。

排查

  • 看拼接结果:打开 /dsh-custom-style/all.css;文件清单与大小:/dsh-custom-style/status。
  • Ctrl+Shift+I → 搜 data-plugin-css="dsh-custom-style" 就是注入的完整样式表; Console 里 [dsh-custom-style] 前缀的日志会报告应用了什么、或拉取为何失败。
  • 规则没生效:先确认选择器命中的是哪个元素(DevTools → 检查),再确认是否需要 !important。
  • 路由 404:宿主半身没加载——检查 <profile>/package.json 的 dsh.profile.bundles 里是否有 dsh-custom-style,然后重启 DSH。

开发

node test-host.mjs                        # 宿主半身:44 项断言(假 ctx / req / res,临时 DSH_HOME)
node test-client.mjs                      # 浏览器半身:59 项断言(假 ModuleLoader / DOM / fetch / React / slots)
npm test                                  # 依次跑完以上两组
node tools/local-preview.mjs              # 本地预览服务(不需要 DSH),见下

不重启 DSH 就预览样式

node tools/local-preview.mjs                                            # 临时样式目录,弹窗从 1200px 开始
node tools/local-preview.mjs --css-dir "$env:USERPROFILE\.dsh\dsh-custom-style"   # 预览你真实的样式
node tools/local-preview.mjs --width 1400 --port 7799

它在 http://127.0.0.1:7788/ 起一个仿真设置弹窗页,驱动的是真实的两个半身:宿主半身拼接样式并 提供 /dsh-custom-style/all.css,页面加载真实的 client.js 完成注入与轮询。 GET /__set?w=1400 会改写预览样式,让你看着两秒重载发生。全程不联网,也不涉及 DSH 本体。

预览页没有 DSH 服务,所以只验证样式注入这一条链路:设置分区需要真实 harness(那里 Console 会打 slots 服务不可用,这是预期的优雅降级)。

开发工具

  • tools/asar/ —— 读取并就地修改出厂 app.asar,用于那些确实无法用 CSS 完成的改动; patch_panel_width.py 以等长改写调整设置弹窗宽度,并可 --revert 还原。 scan-all-maxwidth.py 列出出厂设置 bundle 里所有 max-width 规则(每条独立 seek—— 把过滤后的条目连着读会产生假阴性)。需要设 ASAR_PATH 指向你的 app.asar。

这些工具只是开发辅助,不属于安装的插件(files 字段已把它们排除在 npm 包之外)。

兼容性

DSH 版本状态
0.2.0-rc.2兼容 —— 本插件的开发与验证版本

在 package.json 的 dsh.compatibility.dshReleases 中声明。

许可证

MIT —— 见 LICENSE。

Related plugins