npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-deepseek-style-theme

v2.1.93

Published

DeepSeek official-home style theme for DeepSeek Harness (desktop app and dsh web): WebGL fluid-flow background, liquid glass with SVG edge refraction, glass sidebar and composer, frosted header, running-subagent progress panel, and peak/valley window tint

Readme

DeepSeek Style Theme

为 DeepSeek Harness 复刻 DeepSeek 官网视觉的主题皮肤插件 —— 桌面应用与 dsh web 通用(两端加载的是同一份 lib/client.js,差别只在宿主应用与 profile 的管理方式,见「安装」一节)。

鸣谢

本插件的两块核心视觉——流体背景与玻璃质感——都不是从零写出来的,它们分别来自下面两个上游项目。没有它们,这个主题不会有现在这个样子:

| 上游 | 作者 | 本插件用到了什么 | |---|---|---| | dsh-theme-mineradio | @dhicoc | 流体背景的三段 GLSL 着色器(顶点 / 流场 / 显示)是上游的副本(唯一改动见 2.0.74:mediump→highp,为了让 Intel 核显不再把噪声画成方块);四分之一分辨率流场 + 双 framebuffer 乒乓的求解器结构、玻璃色散折射滤镜、光标视差、对话框悬停倾斜同样移植自上游 | | deepseek-harness-background | @HaoyueQin | 液态玻璃配方的技法来源(半透明填充 + 竖向光泽 + 背板滤镜链的设计思路),本插件按自己的选择器与 --dshome-glass-* 变量重新表达,未复制源码 |

两个项目均为 MIT 许可;完整许可原文见 THIRD-PARTY-NOTICES.md,随包发布,再分发时必须保留。详细的移植范围与改写点见下文「第三方代码与署名」。

特性

  • 流体流动背景(1.42.0+):全屏 WebGL2 双通道流体模拟——四分之一分辨率的流场(衰减 + 带速度的指针笔刷,两个 framebuffer 乒乓)被全分辨率的域扭曲噪声渲染器采样,带旋流迭代与三色柔性混合;按钮悬停会轻推流场、点击则荡开涟漪。配色跟随 DSTT 三色令牌(峰谷红 / 谷时蓝 / 常态绿)实时重新着色,无需重挂。无 WebGL2 时自动回落到原来的粒子背景(粒子实现完整保留)。流场自 2.0.81 起优先存储在 RGBA16F 里(探测 EXT_color_buffer_float + 4×4 探针确认 FBO complete,否则诚实退回 RGBA8)——8 位存储会把每帧衰减舍入到 1/255,弱值卡住、场里长出平台,域扭曲把平台画成"飘动的方块"(实测:同样 240 步,RGBA8 的场均值 0.0172 挤在 13 个台阶上,RGBA16F 正常衰减到 0.00002);页面可直接读 window.__dshomeFluidFormat 确认这台机器拿到的是哪种场
  • 液态玻璃(1.42.0+,1.43.0 起分两套配方):一套「湿玻璃」配方——半透明填充 + 顶部最亮、约 38% 处消失的竖向光泽渐变 + 统一的 blur() saturate() brightness() contrast() 背板链 + 内嵌顶部高光与发丝描边,悬停只提亮填充;所有旋钮都是 --dshome-glass-* 变量。1.43.0 新增 液态 / 白磨砂 二选一(见下文 DSTT 设置):液态 更通透、折射更强、边框带自走高光;白磨砂 就是上面这套。折射(SVG feDisplacementMap)只加在四个区域上(侧边栏 / 对话框 / 标题栏悬停态 / 用户气泡):它仅 Chromium 支持,且每个元素一次滤镜采样
  • 鼠标跟随的对话窗光斑(1.42.0+):光标在对话窗(消息区与输入框)上移动时,窗口上有一团跟随鼠标的柔光;它用 background-attachment: fixed 锚在视口上,所以滚动不会把光斑一起拖走,也不需要覆盖层元素。悬停玻璃面时另有一层随光标偏移的高光(--dshome-spec-x/y)
  • 玻璃拟态:侧边栏、会话卡片、输入框半透明填充 + 毛玻璃
  • 明暗双主题:深色通过 body[data-ds-dark-theme] 切换
  • 品牌细节:胶囊按钮、渐变主按钮、圆角卡片
  • 标题栏:默认透明,悬停变毛玻璃;栏内按钮统一为真圆角(覆盖产品全局 corner-shape:superellipse 造成的方角观感),会话标题小胶囊化
  • 悬停动画:中/E、「打开方式」、「对话 / 轨迹」标签悬停时上浮 + 品牌色辉光;中/E 短标签「中/E」平滑展开为「中文/EN」(max-width 过渡,非 display 硬切);「打开方式」展开菜单时箭头翻转,切换标签时选中项弹出
  • DeepSeek 品牌链接:点击侧边栏 DeepSeek 标识跳转 https://www.deepseek.com/
  • 轨迹视图:「对话 / 轨迹」标签在标题栏悬停(.dshome-swap)或轨迹视图存在时显示,不是常驻——会话视图下实测 [class*="wSkVaW_tabs"] 默认 display:none(2.0.84 按实机报告 B4 勘误)
  • 运行中子代理面板:有子代理启动时,右下角浮出玻璃面板,实时列出正在运行的子代理——名称、已运行时长、token 用量与流动进度条;点击条目直接跳转到该子代理会话,可折叠收起
  • 跨平台:交付文件菜单里的「打开所在文件夹 / 在文件管理器中显示」由官方通路执行(session.openWorkspacePath),各平台调用系统文件管理器;本插件不再自己开文件管理器
  • 模型目录同步:已交还官方(2.1.90 起)。官方自带模型目录(ui-model-selection + modelCatalog),"选择器里有哪些模型"以官方为准;本插件不再介入,也不再写 llm-deepseek.models。
  • 交付文件菜单(1.41.0+,1.41.2 起覆盖两处):回复末尾的**「本轮文件改动」条目左键点击**即弹出玻璃拟态操作菜单,交付文件卡片右键弹出同一菜单——用默认应用打开 / 打开所在文件夹 / 复制路径 / 打开预览(卡片上显示为「在侧边栏预览」);产品自己的下拉菜单里也补上它唯一缺的「复制路径」。产品原本的左键动作被保留成菜单项,不会被丢掉
  • 时段提示(DSTT):主题模式四选一——峰谷红蓝(高峰鲜红、谷时段蓝)/ 峰谷红绿(高峰鲜红、谷时段绿)/ 常态绿 / 常态蓝;高峰窗口北京时间 9:00–12:00、14:00–18:00(周六周日全天非高峰),高峰色为鲜红 #F5222D

DSTT(DeepSeekStyleTheme)设置

设置页新增 DSTT 选项卡,提供一行四选「主题模式」(deepseek-style-theme 段的 mode 字段,持久化到 settings.yaml):

  • 峰谷红蓝(默认):高峰 = 鲜红(提示),谷时段 = 蓝色;
  • 峰谷红绿:高峰 = 鲜红(提示),谷时段 = 绿色;
  • 常态绿:始终绿色,无高峰区分;
  • 常态蓝:始终蓝色,无高峰区分。

语义锚点版(2.1.0+,面向 dsh 0.2.x 官方桌面版)

产品每次构建都会重排 CSS Module 哈希(0.1.7 的 .wSkVaW_* → 0.2.0 的 .Dc7zOa_*), 任何写死类名的补丁都会在更新后静默失效。2.1.0 起主线只依赖产品语义钩子,不再写死哈希:

| 保留(语义) | 依据 | |---|---| | 输入卡玻璃化 | [data-composer-stats] / [data-composer-card] | | 浮层菜单液态玻璃 | [data-menu-material] | | 交付文件卡片 / 文件改动行 | [data-presented-file] / [data-presented-files-row] | | 文件弹窗(打开文件/复制路径/打开文件夹) | button[data-ref-chip] / [data-presented-*] | | 用户气泡 | [data-chat-flow-kind="user"] | | 首屏标题 / 预览角标 | [class*="_headline"] / [class*="_previewBadge"] |

同时把 installGuarded 从「挂载时查一次」升级为 MutationObserver:懒挂载的面板与 晚出现的行也能等到样式,不再有「刷新一下才生效」这一类问题。

这一版的取舍(明确不做)

下列面在 0.2.0 里没有语义钩子,只有每次构建都会变的类哈希,因此不再覆盖:

  • 侧栏外壳的玻璃与折射、应用外框、标题栏悬停胶囊与中/EN 切换定位、光标高光跟随、 会话行/项目行样式、settings 与子代理面板的内部样式。

代价是视觉上少两块(侧栏、外框),换来的是一条规则:产品更新不需要改主题。

2.0.90 那套写死哈希的实现留在 git 历史里,需要时用 git show 193d691:lib/client.js > lib/client.js 取回——不要把哈希版当回退方案: 它的锚点(.wSkVaW_*、.hHd-Xa_*、.YDXeBa_*…)在当前 0.2.0 产品里全部 0 命中, 拿回来同样是没有样式的(实测)。 另有几行开关与选项:

  • 背景方式(backgroundMode,默认 classic;2.0.73+):一行四选,切换即时生效、不用刷新页面。
    • ① 主色+纯白+近白(classic,默认;2.0.90 起):1.43.11 那套(软主色 + 纯白 + 近白),按钮与背景同一色系、没有第二种色相。2.0.83 曾把默认改成 bold,2.0.90 按用户要求改回——默认就是这套「主色 + 纯白 + 近白」;
    • ② 主色+纯白(white):同一软主色 + 纯白。着色器三个 uniform 里第三个复用白色(深色复用该族的近黑),所以只有两个颜色参与;
    • ③ 自选背景(custom;2.0.76 起的分工见「插件商城适配」):我们这层完全不画——流体/粒子画布整体卸载,主题自己的 per-colour 底色也按选择器让位(带 :not([data-dshome-bg=custom]),不是拿 !important 对打),深色蒙版只用在你自己填的图片上。背景到底是谁的:装了 dsh-plugin-wallpaper-engine 且它正在渲染时连纯白都不画,背景交给它;没有它时是纯白(浅色)/ 纯黑(深色)。颜色(蓝/绿/红)只驱动按钮与品牌 token,不参与背景。想放自己的图就填图片 URL / 任意 CSS 背景值,或点「桌面壁纸」(写入 desktop,由宿主端读本机壁纸,见隐私一节);不合法的值不会白屏;
    • 浓三色(bold):1.43.12 的三浓色,保留可选(用户要求先留着)。
  • 动态背景(流体)(ambientBackground,默认开;2.0.75+):关掉后不挂载 WebGL2 流体,也不回落到粒子——不是把它藏起来(藏起来的画布照样在算),而是根本不创建,GPU 占用归零,背景只剩静态配色。集成显卡把流体画成方块、或风扇被它拉满时,关这个。
  • 流体跟随笔刷(fluidBrush,默认关):开启后光标才把速度写进流场、拖出尾迹;改完刷新页面生效。
  • 玻璃风格(glassStyle,默认 液态):
    • 液态(1.43.0+):Apple Liquid Glass 方向——填充降到 rgb(255 255 255 / .14)、顶部光泽降到 .07,背板链改为靠 saturate(1.75) 让身后颜色发亮,共用的 feDisplacementMap 走更大的 scale(104,白磨砂仍是 60),边框上再跑一圈自走的 conic-gradient 高光(@property 注册的 <angle>,与鼠标无关)。上玻璃的有四处面板:侧边栏外壳 / 对话框 / 标题栏(悬停)/ 用户消息气泡;另有第五处浮层见下一条;
    • 浮层菜单也上玻璃(2.0.90+):产品把下拉菜单 portal 到所有玻璃面板之外,菜单自带的材质又铺了一层不透明的 rgba(248,249,250,.58),所以液态会话里菜单是唯一一片不透光的白(代理预设 / 模型 / 权限 / 会话行菜单都算)。这些菜单由 MENU_GLASS_CSS 单独接管,锚在产品自己的材质钩子 [data-menu-material] 上——只加玻璃身份,不重画材质:外圈是主题的 --dshome-liquid-shadow + --dshome-liquid-ring 内描边,圆角用 --dsw-radius-lg,菜单里的填充与可读性、以及产品自带的 blur(40px) saturate(1.5) 背板全部原样保留。白磨砂档位下只加一圈 --dshome-glass-ring,两套配方的其余样式照旧互不干扰;
    • 白磨砂:1.42.x 那套白磨砂 + 蓝变光边框,样式逐像素不变(新配方整块挂在 html[data-dshome-glass="liquid"] 下,已用 1991 个元素的计算样式签名验证过:切到白磨砂时新样式表贡献为零)。唯一的例外是悬停倾斜:它自 2.0.82 起两套配方都生效(原先只在液态下挂),运动与配方无关——见「已知产品侧缺陷」一节;

高峰判定(1.37.0+)

  • 固定按**北京时间(UTC+8)**判断,与机器本地时区无关;向上取整到整分钟(13:59:xx 视为 14:00)。
  • 高峰窗口:9:00–12:00、14:00–18:00(周六、周日全天非高峰)。峰谷模式下高峰自动变鲜红(事件链计时:启动/切换时监测一次,记录距高峰结束的差值并计时还原谷色;非高峰则计时到下一高峰开始变红),退出高峰自动还原。
  • 切换彻底:背景光效、按钮、输入框边框、卡片旋转描边、粒子、产品品牌 token(--dsw-alias-brand-primary 等)全部跟随品牌变量,无残留色。
  • DSTT 面板状态行实时显示「距高峰结束还剩 X 小时 X 分」。

插件商城适配(与壁纸 / 侧栏插件共存)

本主题与商城里的两个热门插件同装一套时会出现「谁画背景、谁管侧栏」的重叠,2.0.76 起按下面这套约定共存。原则:只读探测、不做 !important 对打、不碰它们的 DOM。

与 dsh-plugin-wallpaper-engine(壁纸引擎,实测 0.7.2)

它把壁纸画在 .we-layer{position:fixed;inset:0;z-index:-2} 里 —— 在 body 背景之上、内容之下,也就是说我们在 <body> 上画的任何东西都会被它盖住;反过来,如果我们的流体画布(z-index:-1)还在跑,就会盖住它的壁纸。

  • 探测:只读它自己的标记 body[data-we-wallpaper](它渲染时置上、停止时移除;本插件用 MutationObserver 跟随,无需刷新)。不读它的设置、不改它的 DOM。

  • ③ 自选背景下的分工(2.0.76 语义):

    | 状态 | 背景由谁画 | |---|---| | ③ + 壁纸引擎正在渲染(body[data-we-wallpaper]) | 它。我们的流体卸载、主题底色让位、连纯白也不画 | | ③ + 没有壁纸引擎 | 我们:纯白(浅色)/ 纯黑(深色) | | ③ + 输入框填了图片 / CSS 值 / 点「桌面壁纸」 | 我们(注意:此时它仍在壁纸引擎图层之下,若壁纸引擎在跑就看不到) | | ①②/浓三色 | 我们(per-colour 渐变 + 流体),与从前一致;若壁纸引擎正在渲染,面板会出现橙色提示「切到 ③」(提示,不自动切) |

  • 让位是"选择器让位",不是 !important 对打:主题自己的四条背景规则带 :not([data-dshome-bg=custom]),进 ③ 就不匹配,所以壁纸引擎自己的 body[data-we-wallpaper] 样式不会被我们压掉。

  • 提示而不是自动切换(2.0.77):检测到它在渲染、而当前档位不是 ③ 时,DSTT 面板出现橙色提示 +「切到自选背景」按钮;我们绝不替用户改档位(标记的 observer 只重渲染面板,不调用改档位接口,冒烟里有断言钉住)。

  • 不抢它的玻璃:它用 data-we-glass-window / data-we-sidebar-glass 管设置窗与侧栏玻璃;这两块我们不插手(若两边同时开玻璃,以它为优先是推荐配置)。

  • 它的前置条件:DSH ≥ 0.1.5-rc.1(Desktop ≥ 2.0.7)且 dsh-better-sidebar ≥ 0.19.0;本主题与这组版本同装验证过。

与 dsh-better-sidebar(右侧栏,实测 0.19.1)

它是 VSCode 式右侧栏(资源管理器 / 编辑器 / 终端 / git / 浏览器),并对外暴露 ctx.betterSidebar 服务。

  • 探测:只读 DOM 标记 [data-dsh-better-sidebar](它的面板宿主)与 [data-dsh-center-col](它给中心列打的标记);宿主侧无需任何配合。
  • 布局:它用 #root [data-dsh-center-col]{margin-bottom:var(--dsh-sidebar-height)} 把会话列顶起来;本主题不写中心列的尺寸、不动它的 tab 栏 / 面板内部,只对产品自己的表面(侧栏外壳、对话框、标题栏)做玻璃与配色。
  • 已知交互(重要):它的面板宿主几何同步会在检测到页面级 transform 时降级(它自己会打 data-dsh-panel-host-degraded)。本主题的「对话框悬停倾斜」会给对话框卡片写 transform: perspective(800px) … scale(1.01) —— 这是卡片级而非页面级,正常情况无碍;但若你在同装后看到悬停抖动 / 底部滑块闪动,先按上一节的顺序二分:__dshomeTilt() 看倾斜此刻是否在动、tilt(false) 一行冻结它、再 ambient(false) 关掉动态背景画布(2.0.82 起两套玻璃配方都会倾斜,所以"切白磨砂"不再是有效的二分手段)。
  • 右侧栏配色:它面板内部的配色由它自己管;本主题的深色规则只覆盖产品原生的右栏表面(.VOzbGW_panel 等),不改它的 class 命名空间。

适配清单(写给以后接手的人)

| 我们读的东西 | 谁写的 | 用途 | 失败时会怎样 | |---|---|---|---| | body[data-we-wallpaper] | 壁纸引擎 | ③ 里决定"画纯白"还是"完全不画" | 读不到就当没装:③ 画纯白/纯黑 | | body[data-ds-dark-theme]、theme 服务 | 产品 / 主题服务 | 明暗标记(不依赖产品属性名,见健壮性一节) | 退回主题服务;再不行按 data-dshome-dark 缺省=浅色 | | [data-dsh-center-col]、[data-dsh-better-sidebar] | better-sidebar | 只用于"不越界"的文档与自查,不改写 | 什么都不做(我们本来也不碰) | | body[data-dshome-*] | 本主题 | 自己的全部开关 | —— |

适配的原则是单向只读:本主题从不写别的插件的属性,也不依赖它们的内部 class(散列类名会随它们重建失效——本仓库就吃过 .gdEzaW_bubble 失效没人发现的亏)。

已知产品侧缺陷与主题的处置(2.0.78+)

悬停消息时的横向溢出 → 对话框 8px 抖动 + 底部滑块闪动

现象:鼠标在对话框右端(发送键 / 小圆圈)附近移动时,两颗按钮的观感来回跳,底部有滑块一闪一闪。

定位链(真机采样,2026-09-15):

  1. 会话滚动区 wSkVaW_scrollBody 出现横向溢出:scrollWidth 917 > clientWidth 891,溢出元素是消息行的 span._bubble_* —— 产品在悬停消息时展示的操作栏把那一行撑得比列还宽;
  2. 横向溢出 → 8px 细横向滚动条被生成 → 同一滚动区的 clientHeight 842 → 834;
  3. 对话框固定在该滚动区下方 → 卡片 top 699 → 691(−8px);
  4. 指针因此落到另一个元素上 → 操作栏收起 → 滚动条消失 → 对话框落回 → 回到第 1 步。~5Hz 两态自激,表现出来就是"按钮跳 + 滑块闪"。

主题的处置(2.0.78):给会话滚动区加 overflow-x: clip(守护块,锚点 .wSkVaW_scrollBody,规则按 [class*="scrollBody"] 子串匹配,散列类改名也不会静默失效)。横向不再产生滚动条,那 8px 就不存在,闭环断开;每个代码块自己的横向滚动不受影响。代价:超出列宽的部分会被裁掉而不是可滚动 —— 在聊天列里这是更小的恶。溢出的根因在产品侧,值得上报。

第二半:我们自己的倾斜(2.0.79 想修,2.0.82 才算修对)。滚动条链条断掉后,真机逐帧采样显示剩下的抖动来自悬停倾斜本身:

card=700/757 … under=div.uV2eYG_row         hovCard=1
card=700/765 … under=button.uV2eYG_primary  hovCard=1     ← scale(1.01) 把卡片从 757 推到 765
card=699/761 … under=div.wSkVaW_widthHandle hovCard=0     ← 边缘越过了指针 → 底下换成拖拽把手
card=700/757 … under=div.wSkVaW_widthHandle hovCard=0     ← 倾斜解除、卡片缩回

指针静止时,1% 放大把卡片边缘推出 8px,于是指针底下的元素在"对话框控件"与"列的宽度把手"之间翻转,而每次翻转都会重新武装或解除倾斜 → 因为过渡只有 0.1–0.24s,比被滚动条中介的那条更快。

2.0.79 修错了:它把抬升去掉(TILT_SCALE: 1.01 → 1),并把 input / textarea / [contenteditable] 也算成"不能压在动面上的控件"。两条都是矫枉过正,而且同因:输入框几乎盖满整张卡片,把它排除等于让倾斜在"鼠标放到对话框上"这个它唯一存在的姿势里永不触发,抬升也跟着一起没了。用户报回来的是「鼠标放在对话框,放大、倾斜没了」——修法本身成了新的缺陷。

2.0.82 的修法(保留效果,切断回路):

  1. 抬起回来:TILT_SCALE 恢复 1.01;
  2. 输入框上可以倾斜:TILT_CONTROLS 只留 button / [role=button] / a[href] / [class*="andle" i],文字录入不再挡。倾斜是带着输入框一起动的,指针不会因此掉出输入框;
  3. 滞回取代"越界即释放"(关键):指针进入控件时冻结当前角度——不重画、不回弹、不重新武装;回到卡片空白处再续;只有指针离开卡片自己的矩形才释放,释放后还有 TILT_COOLDOWN_MS = 200 的冷却期才允许下一次进入。那条自激回路需要"释放"来喂它,而现在指针静止时倾角是死的,没有任何反馈通路。
  4. 两套玻璃配方都倾斜:原先 paint / onOver 各有一道"不是液态就早退"的闸门(理由是白磨砂要保持逐像素不变),结果是用白磨砂的人永远看不到这个效果——用户点名的第二条要求就是「白磨砂玻璃也要有」。现在闸门拆掉,而且运动自带过渡(TILT_TRANSITION,值与原液态样式表里那条相同),这样任何配方都不会因为少了一条 transition: transform 而变成硬跳;
  5. 状态可从页面直接读:window.__dshomeTilt() → { recipe, leaning, frozen, engagements, sinceRelease }。"效果不见了"因此不再需要猜是哪道闸门吃掉的。

排查方法记在这里,方便复现:ResizeObserver 盯住卡片到 body 的祖先链(谁改高度谁自己报),再对滚动区打 scrollWidth/clientWidth 与"右边缘越界元素";最后用 rAF 逐帧记录"指针底下是谁 + 候选元素的矩形",翻转对就能一眼看出。跨机器复现用 tools/symptom-probe.js(贴进控制台,run(秒) 同时采样 GPU 状态与光标下的逐字段 diff,tilt(false) 一行就能把倾斜冻结做对照)。

健壮性设计(1.33.1+)

产品壳的构建期 hash 类名(如 wSkVaW_header)不是 API,产品升级重构建后可能变化。本主题对此做了分层防护:

  • 锚点守卫 CSS:所有针对产品 hash 类名的样式拆成独立补丁块,只有对应锚点类存在于 DOM 时才注入(延迟 30s 等待晚渲染壳),锚点永久缺失时跳过该块并打一行诊断日志,而不是静默注入失效 CSS;
  • 自有暗色标记:明暗模式由主题服务的 active.colorScheme 驱动到自有属性 data-dshome-dark,不依赖产品属性名;
  • 能走 Slot 的 UI 走 Slot:语言切换注册在 conversation.session.header.utilities(产品公开 Slot API);仅无 Slot 的表面(工作区行菜单、交付文件卡片)使用 DOM 补丁。
  • 逐层降级而不是整块失效(1.42.0+):背景先探测 WebGL2,拿不到就用粒子实现(startParticles 完整保留),着色器编译/链接失败同样返回空操作句柄而不是抛错;折射滤镜只在引擎真的支持 SVG backdrop-filter 时才挂 data-dshome-dispersion,其余浏览器静默保留普通毛玻璃;连 backdrop-filter 都不支持时由 @supports not (...) 回落为不透明填充。
  • 玻璃的开销在哪,是量出来的(1.43.0):压测(复制 30 个真实气泡、同屏 9~10 个)显示 SVG 折射滤镜几乎免费(带/不带 feDisplacementMap 差 0.9fps),真正花钱的是 backdrop-filter 这个能力本身(去掉它 63 → 80fps);不上玻璃则是 124.8fps。当前配方在同屏 9~10 个玻璃气泡下仍有 ~60fps(p95 20.9ms,无超过 32ms 的帧),而侧边栏 / 对话框 / 标题栏三处在无压测时与白磨砂完全同速(94.4 / 94.6 fps)。同屏用户气泡通常是 2~5 个,所以不为此牺牲观感。数据与四个变体的完整表格见 CHANGELOG.md 的 v1.43.0 条目。

dsh.client.inject 的语义:加载顺序,不是 import

package.json 的 dsh.client.inject 声明了三个产品包,但 lib/client.js 里没有 import、也没有 export——它是 module.exports = function (ctx) {...} 的工厂形式,由 window.__ModuleLoader__.load() 注册。这条声明的作用是加载顺序:dsh-client-modules 的浏览器端会在本插件的 factory 运行之前先把这三个 bundle 注册进来(arriveGraphRow),于是工厂里 ctx.get('theme' | 'locale' | 'connection') 必定拿得到服务。

它不是模块图依赖——模块图的边是 dsh.client.external(orderByModuleGraph 只读 external)。因此:

  • 不要为了这条声明去 import 这些包,那是另一套机制;
  • 目标包不存在时会被静默跳过,不会卡住启动;
  • 但它确实有用:把「factory 里要用到的服务」列全,就不必自己写等待逻辑。

安装

装在哪一端?先看这张表 —— 两端加载的是同一份插件代码(lib/client.js 跑在渲染进程里),差别只在宿主应用与 profile 的管理方式:

| 你用的是 | 怎么装 | 为什么 | |---|---|---| | 桌面应用(@deepseek-ai/dsh-desktop) | 从应用内置的插件市场安装(设置 → 插件市场) | 桌面端的 profile 由 Electron 应用独占管理,CLI 会直接拒绝:profile "desktop" is managed exclusively by the Electron application。所以 dsh plugin --profile … 这条路在桌面端不通 | | dsh web(命令行起的 web 端) | dsh plugin --profile web add dsh-deepseek-style-theme | 这一端的 profile 归 CLI 管,--profile web 是正确的 |

插件市场里那行 dsh plugin --profile web add … 是市场按默认 profile 生成的,只对 dsh web 成立;桌面端照它敲会装进一个桌面应用根本不读的 profile。

前置要求(仅 dsh web 端需要):node 与 pnpm(dsh plugin 命令只是 pnpm 的转发器,二者需在 PATH 中)。安装后重启对应的应用即可生效。

安装属性(第三方):本插件是第三方适配的皮肤插件,不是 DeepSeek 官方插件。所有安装方式(本地源码 / GitHub / 本地 tgz)默认都作为第三方包装入 profile 的 node_modules(真实目录,file: 依赖)并登记进 dsh.profile.bundles——绝不写入或链接官方 dsh 安装目录(如全局 CLI 的 node_modules,官方升级会清空其中的第三方包)。若因旧版链式安装导致插件消失/失效,重跑一次 dsh plugin --profile web add <源> 即会以独立目录重建。

先读:四个高频坑

| 你看到的 | 真正的原因 | 正确做法 | |---|---|---| | 装完 dsh web 直接崩:Cannot find package '@deepseek-ai/schemastery' | 用 link: 装了(见下条) | 改用 file: | | 同上 | 1.40.0 及更早把 schemastery 声明为 peer,而 profile 模板下发的是 autoInstallPeers: false,peer 永远不会被安装 | 升到 1.41.0(它是正式依赖,会被自动装上),或手动补装 | | add . 装完反而解析不到依赖 | . 会被展开成你当前所在目录的绝对路径;源码在 profile 之外时这正好落成 link: | 不要用 .,用下面的 file: 写法 | | github: 安装报 TLS / 证书错误 | pnpm 需要执行 git clone,带 TLS 中间人代理的环境必然失败 | 改用本地 tgz 或本地目录 |

为什么 link: 一定不行:link: 落成 junction / symlink,而 Node 的 ESM 解析是从文件的真实路径向上找 node_modules。真实路径在 profile 之外时,就永远走不到 profiles/<name>/node_modules,插件的依赖会全部解析失败——即使那些依赖已经装在 profile 里。file: 会把源码拷进 profile,是唯一稳妥的做法。

Windows 上不要用 inject::inject:D:\... 会因为盘符里的 : 被 pnpm 当成 registry 协议分隔符,去 registry 找一个名叫 inject:D:\... 的包并拿到 404。Windows 一律用 file:。

安装方式

从 npm(推荐)

dsh plugin --profile web add dsh-deepseek-style-theme

这是唯一计入 npm 下载量的安装方式。 下面几条(file: / github: / 本地 tgz / 任意 git 源) 都绕开 registry,装多少次都不会让 npm 的统计动一下——邀请别人试用时请发上面这一条。 它同时是最省事的:不走 git clone(绕开代理 / 证书问题),也不依赖 GitHub Release 资产 (绕开 releases/latest 那类 URL 腐烂)。

关于包名 包名是无 scope 的 dsh-deepseek-style-theme。这是刻意的:@dsh-external 这个 scope 在 npm 上属于别人(wulei1107,下面挂着 @dsh-external/dsh-vision-toolkit),不是本项目的命名空间,也发不进去。

改名的三个引用点必须同时对齐,缺一个都会静默失效,且失效方式各不相同:

| 位置 | 不对齐的后果 | |---|---| | package.json 的 name | 包装不进去 / 目录名不符 | | cordis.patch.yml 的 name: | Loader 解析不到模块,插件装配失败 | | lib/client.js 里 __ModuleLoader__.load 的 id | bundle 加载成功却什么都没注册,客户端半边失效(1.43.1 的缺陷) |

自查:grep '@dsh-external' lib/ 应当一处都不命中;命中即漏改(有意保留的兼容位置除外)。

从本地源码(开发模式,不计入下载量)

# 源码放哪个稳定目录都行(file: 会把它拷进 profile)
dsh plugin --profile web add file:D:\plugins\dsh-deepseek-style-theme

不要 cd 进插件源码再执行 add .——. 会展开成调用目录的绝对路径,落成 link:,于是踩中上面的坑。

从 GitHub(本仓库,不计入下载量)

dsh plugin --profile web add github:Lichtspur/deepseek-style-theme

本插件是纯 JS 且 lib/ 已提交到仓库,Git 安装无需构建——不会触发 pnpm 的 prepare/allowBuilds 授权流程,一次 add 即可生效。GitHub 安装的包同样默认落在 profile 第三方插件区(独立目录),不会进入官方 dsh 安装目录。每个版本对应的 tgz 归档附在 GitHub Releases 页面。

代理 / 企业网络:github: 安装要靠 pnpm 调 git clone,在带 TLS 中间人证书审计的网络里会以证书错误失败——Windows 上是 schannel: AcquireCredentialsHandle failed: SEC_E_NO_CREDENTIALS (0x8009030e),改用 OpenSSL 后端后变成 unable to get local issuer certificate (20)。报错信息只看得到 TLS,很容易误判成插件的问题。这类环境请直接用下面的 tgz 或本地目录安装。

从本地 tgz(推荐用于代理 / 离线环境,不计入下载量)

dsh plugin --profile web add ./releases/dsh-deepseek-style-theme-1.43.1.tgz

releases/ 下的归档随每个 Release 发布,既不依赖 git 也不依赖 registry,在受限网络里最省事。 归档文件名由 package.json 的 name 与 version 决定(pnpm pack / npm pack 均如此), 因此 1.44.0 起是 dsh-deepseek-style-theme-<版本>.tgz。

从任意 Git 仓库(不计入下载量)

dsh plugin --profile web add <git-url>

若该仓库未提交构建产物,则需其提供 prepare 脚本;按 pnpm 提示在 ~/.dsh/profiles/<name>/pnpm-workspace.yaml 的 allowBuilds 中授权后重跑。

依赖是怎么解析的(排错用)

  • 1.41.0 起 @deepseek-ai/schemastery 是本插件的 dependencies(唯一运行时依赖),dsh plugin add 会把它一起装进 profile 的 node_modules。@deepseek-ai/cordis 只声明为 peer:插件并不 import 它,运行时由宿主提供。
  • 它失败时不会再拖垮整棵插件树:lib/index.js 用受保护的动态 import() 加载 schemastery,拿不到就只丢一行警告、DSTT 模式不再持久化,主题其余部分照常工作(1.41.0 起)。
  • 不要指望 .dsh-module-fallback/node_modules 帮忙补依赖:在从未装过依赖的 profile 里它一直是空的,bundle 实际是从 CLI 安装目录(如全局 npm 的 node_modules)解析的。所以插件依赖必须真的装在 profile 里。

必要权限

  • 安装期:需要修改 web profile(~/.dsh/profiles/web/ 的 package.json、dsh.profile.bundles 装配列表与 node_modules);Git 安装还需按上文授权 pnpm 执行构建脚本。
  • 运行时(DSH 审批/沙箱):不需要任何额外权限——不注册工具、不执行模型调用、不请求审批或沙箱提升。它只是纯前端皮肤 + 一个受限 RPC 通道。
  • 出网动作:没有。2.1.90 起本插件不发起任何外部请求——原先"插件启动时向端点同步一次模型目录"的那一条,已随该功能一并移除。
  • 系统级副作用:交付文件卡片的操作菜单有四个动作。「打开文件」与「打开所在文件夹」优先走官方通路 session.openWorkspacePath({ path }) / ({ path, action: 'reveal' }) —— 宿主侧自己校验路径、解析工作区映射,拿不到已验证映射时抛错而不是猜。「复制路径」纯前端写剪贴板,不经过宿主端。只有在官方这条远程不存在时(旧内核),前两者才回落到本插件自己的私有通道 dshome/file.open(Windows Start-Process、macOS open、Linux xdg-open)与 dshome/file.reveal(Windows explorer /select、macOS open -R、Linux 打开所在目录),那条通道同样只接受本机绝对路径、走同一套围栏。「打开工作区目录」已在 2.1.90 移除:那个手势改用官方的 open-in-app,本插件不再自己开文件管理器。
  • 私有 RPC 通道的围栏(1.41.0 起与 dsh 核心 /api 同一套判定):只接受
    1. 回环对端(127.0.0.1 / ::1)——该通道一直如此;
    2. Host 为回环权威(127.x / [::1] / localhost),挡掉 DNS rebinding;
    3. Sec-Fetch-Site 非 cross-site,且 Origin 存在时必须与 Host 同源——挡掉跨站 fetch(简单请求不触发预检,副作用仍会执行);
    4. Content-Type: application/json——让跨站尝试必须预检,而本路由从不回应预检;
    5. payload.path 必须是本机绝对路径:相对路径、含 NUL、UNC(\\host\share、//host/share)一律拒绝。UNC 会让 Windows 向该主机做认证,可被用来外带 NTLM 哈希。
  • 数据可见性:客户端读取会话列表元数据(标题、运行状态、token 用量)仅用于页内展示「运行中子代理」面板,不上传任何外部服务器;唯一的网络跳转是点击品牌标识时打开 deepseek.com(显式用户操作)。宿主端的唯一请求是上一条所述的 GET {baseURL}/models,只发往你配置的 DeepSeek 端点,不携带会话内容。
  • 桌面壁纸(2.0.75+,只在显式选择时触发):只有当你在 ③ 自选背景里把输入框填成 desktop(或点「桌面壁纸」按钮)时,宿主端才读一次本机壁纸——优先 Windows 的 TranscodedWallpaper(当前实际显示的那张),退回注册表 HKCU\Control Panel\Desktop\WallPaper,再退回注册表的纯色值——并把图片经同源围栏下的 GET 子路径发给本页当 CSS 背景。该端点不接受任何输入(没有路径参数、没有查询串),所以它能供出的永远只有宿主自己解析到的那张图;响应带 Cache-Control: no-store,壁纸换了刷新即见。图片只在本机回环上传输,不上传、不缓存、不落盘,会话记录里也不会出现它的内容。网页本身没有任何 API 能访问系统壁纸,所以这一步只能由宿主端做。留空不会触发它(留空 = 纯白/纯黑,或让位给壁纸引擎)。
  • 配置落盘:DSTT 主题模式写入 profile 的 settings.yaml(deepseek-style-theme 段)。

卸载

dsh plugin --profile web remove dsh-deepseek-style-theme
  • 该命令移除依赖并自动从 dsh.profile.bundles 装配列表剔除,重启 dsh web 后主题完全消失。
  • 卸载即净:插件停用时,其注入的 CSS、粒子画布、运行中子代理面板、DOM 补丁与 RPC 通道全部自动移除,刷新页面即可,无需清理浏览器缓存。
  • 残留清理(可选):若 profile 的 cordis.patch.yml 中有 - id: ui-skin-deepseek-style 残留行可手动删除;settings.yaml 中的 deepseek-style-theme 配置段亦可在设置页或直接编辑文件删除。

自测工具(开发用,不随包发布)

tools/ 里的脚本都在本机直接跑,不进发布产物(package.json 的 files 只含 lib/、cordis.patch.yml、README.md、LICENSE)。除 gui-probe.mjs、symptom-probe-selftest.mjs 与 --open 的 bridge-smoke.mjs 外,都不需要浏览器、不需要网络。

| 工具 | 作用 | 运行 | |---|---|---| | symptom-probe.js | 贴进浏览器控制台的现场探针(2.0.81+):专门给「症状机器不是开发机」的情况用。先打印一份环境 JSON——每个补丁块是否落到 DOM 上(style[data-plugin-css] 清单 + 13 个锚点是否存在:锚点按产品构建哈希命名,换构建就会静默跳过,2.0.78 的 overflow-x: clip 就是靠它守住的)、data-dshome-* 标记、每块 canvas 的 z-index/opacity/所在 GPU/是否 context lost、消息列的 scrollWidth - clientWidth 与超宽/自溢出的子元素、发送按钮等控件的盒模型与 transition;再给 run(秒):同时采样 GPU 状态(fps、context lost、gl.getError()、canvas 尺寸抖动)与光标下的逐帧变化(命中元素链、盒、transform、padding、margin 的逐字段 diff,带 240 条截断与变化率),最后是 tilt(false) / clip(true) / ambient(false) / wallpaper(false) / glass(false) 五个开关,用来把「关掉 X 之后症状还在不在」变成一句话答案。不写任何设置、不弹权限框 | 控制台粘贴整份文件 | | symptom-probe-selftest.mjs | 上面那份探针的自测(19 项):在一个合成页面上跑(我们自己的 canvas + 带超宽子元素的消息列 + 每 60 ms 改一次 transform 的卡片 + CDP 派发真实鼠标移动),断言环境报告的字段形状、run() 的采样与逐字段 diff、五个开关的实际效果(含 clip 在 overflow-y:auto 旁会计算成 hidden 这条 CSS Overflow 3 规则) | chrome --headless=new --remote-debugging-port=9333 --user-data-dir=%TEMP%\dsh-probe-chrome about:blank,然后 node tools/symptom-probe-selftest.mjs | | bridge-smoke.mjs | 宿主端私有 RPC 通道:协议、错误路径,以及完整围栏(非回环对端 / 缺 Host / DNS rebinding / 跨站来源 / Origin 不匹配 / 非 JSON 类型 / UNC 路径 / file.* 端点识别与校验,24 项检查;加 --open 为 25 项,会真的用系统默认应用打开一个临时文件,会在桌面弹出窗口,默认不启用) | node tools/bridge-smoke.mjs [已安装的 lib/index.js] [--open] | | dstt-schema-smoke.mjs | DSTT 设置 schema 与写入路径的一致性:每个能写进 settings.yaml 的值都必须过 register() 的校验(1.43.2 那次 schema 缺 wide 的发布阻塞就靠它守)。含背景方式四档的往返、未知 id 拒写、自定义背景的归一化(控制字符→空格、去首尾、长度上限)、动态背景开关的读写与布尔回读,以及「客户端枚举 = 宿主枚举」的跨文件检查(29 项) | node tools/dstt-schema-smoke.mjs [lib/index.js] | | bg-recipes-smoke.mjs | 客户端源码层的数据与契约断言(2.0.73+,随每版增补):① 必须含纯白 + 近白、② 只有两个颜色参与、bold 浅色无 #FFFFFF、③ 的基底/让位规则与选择器守卫、壁纸引擎标记与「提示而非自动切换」、桌面壁纸端点与 WallpaperStyle 映射、darkSync 作用域、两条反引号守卫(整表扫描)、发送按钮 hover 不再位移、流场 16F 探测与回退、悬停倾斜的抬升 1.01 与 TILT_CONTROLS 不含文字录入、进入控件时冻结而非释放、释放只在离开卡片矩形、两套玻璃配方都生效(2.0.82)、横向裁切覆盖消息列/对话框/转写区、8 个 CSS 契约名必须已声明 + 模板插值不得早于 const 声明(2.0.84:ATTR 被误删、USER_BUBBLE 提前插值都属于这一类,node --check 看不见)(73 项) | node tools/bg-recipes-smoke.mjs [lib/client.js] | | parse-smoke.mjs | 每个文件都必须能解析(2.0.84+):lib/ 与 tools/ 下每个 .js/.mjs 过一遍 node --check。看着多余,其实不是——已经有三次"注释里的反引号把模板字符串提前结束"的事故(lib/client.js 两次、tools/gui-probe.mjs 一次,后者 Node 的报错还不带文件名)。纯解析门,不做 lint | node tools/parse-smoke.mjs | | fluid-precision-test.mjs | 流场存储精度对照实验(2.0.81+):把 lib/client.js 里的真实 VERTEX_SHADER / FLOW_SHADER 抠出来,在无头 Chrome 里对同一场景各跑 240 步,分别存 RGBA8 与 RGBA16F,读回统计"取值分布 / 落在 1/255 网格上的层级 / max / mean",并判定 16F 是否真的摆脱了量化(5 项)。需要 Chromium 系浏览器,且 Chrome 在受限进程里起不来,请从普通终端跑 | node tools/fluid-precision-test.mjs [--chrome <路径>] [--keep] | | gui-probe.mjs | 无头浏览器直连 CDP 量实时页面:主题注入了哪些样式与补丁块、标题栏几何与子元素 flex order、标题栏下方带边框元素与逐行亮度扫描(1px 横线会表现为数值尖峰)、各胶囊的 corner-shape、中/E 悬停前后、对话/轨迹标签、--models 时的模型选择器选项、--file-card 时合成两张交付表面并验证左键/右键菜单(条目、定位、复制提示、预览转发、选中后关闭)、--deliverables 时报告真实页面上两处交付表面的存在情况与点击后果、--ambient 时报告背景到底是流体还是粒子回落、折射是否挂上、玻璃配方是否生效,并悬停一次验证 --dshome-spec-x/y 真的在写、--glass 时逐个报告玻璃面的计算样式与 ::before/::after 是否已被产品占用、--messages 时报告气泡的类名分组、祖先对齐链与 data-chat-flow-kind 取值表(气泡的 role 判定就靠它)、--shot <目录> 时落一张全页截图,--pre <文件> 可在截图前先跑一段页面侧表达式(--pre-arg 作为 window.__preArg 传入),用于拍出「指定玻璃配方 × 指定明暗」的矩阵 | 见下 |

# gui-probe:先起一个监听 CDP 的 Chrome,再把 dsh 签名用的会话密钥放进环境变量
chrome --headless=new --remote-debugging-port=9222 --user-data-dir=%TEMP%\probe about:blank
set DSH_PROBE_SECRET=<$DSH_HOME/.credentials.yaml 里 client-connection/browser-session 的 secret>
node tools/gui-probe.mjs --models --file-card
# 可选:--url http://127.0.0.1:3080  --cdp http://127.0.0.1:9222

两个涉及密钥的工具都只从环境变量读,不碰凭据库,也不会把密钥打印出来;未设置时直接以 exit 2 退出并说明怎么取。

目录结构

.
├── package.json                    # dsh bundle 元数据
├── cordis.patch.yml                # insert 插件行
├── THIRD-PARTY-NOTICES.md          # 第三方许可原文(mineradio / harness-background)
├── releases/                       # 发布产物(tgz 归档)
├── tools/                          # 自测工具(不随包发布)
│   ├── bridge-smoke.mjs            # 宿主端私有 RPC 通道冒烟测试
│   ├── symptom-probe.js            # 贴进控制台的现场探针(环境报告 + 采样 + 逐帧 diff + 开关)
│   ├── symptom-probe-selftest.mjs  # 上面那份探针的自测(合成页面 + CDP 真实鼠标)
│   └── gui-probe.mjs               # 实时页面的无头浏览器探测与像素扫描
└── lib/
├── index.js                    # host 端:交付文件 RPC、DSTT 设置、桌面壁纸
    └── client.js                   # 主题 client 端(含流体背景、液态玻璃、交付文件卡片菜单)

许可

MIT

第三方代码与署名

本插件的流体着色器、玻璃折射滤镜与光标视差移植自 dsh-theme-mineradio v2.3.8(MIT,Copyright (c) 2026 John Wu)——三段 GLSL 是上游的副本,只改了命名与外部依赖,外加 2.0.74 记录在案的一处改动:precision mediump float; → precision highp float;(FLOW_SHADER 与 DISPLAY_SHADER),原因是 Intel 核显在 ANGLE/D3D11 下把 mediump 当真 16 位浮点,sin 哈希噪声会塌成方块;WebGL2 保证片元着色器支持 highp,在把 mediump 提升为 32 位的 GPU 上此改动为空操作。1.43.0 的对话框悬停倾斜同样移植自上游 startSpotlight 的 tilt 分支(常量 TILT_MAX 0.0175 / TILT_PERSPECTIVE 800 / scale(1.01) / 松手 240ms 后清内联属性,按本主题只作用于对话框)。液态玻璃配方则仿照 deepseek-harness-background 的玻璃样式(MIT,Copyright (c) 2026 HaoyueQin),是按本插件选择器与 --dshome-glass-* 变量对该技法的重新表达,未复制其源码。

两份完整许可原文见 THIRD-PARTY-NOTICES.md,随包发布,再分发时必须保留。

另一条需知的来源链:上游注明其流体着色器本身是 deepseek.com 站点 bundle 中 ds-join-shader-bg 的逐字移植。该来源说明为上游所写,记录于此以便审计。

本插件为第三方主题皮肤,复刻 DeepSeek 官网视觉风格,与 DeepSeek(深度求索)官方无隶属关系、无背书或关联;DeepSeek 为深度求索公司的商标。