发布时间:2026/8/22 17:20:50
给 DeepSeek Harness 写了个 web UI 插件 摘要AI 编程工具的插件生态大家都在写工具型插件。但想给界面换个皮肤、加只悬浮宠物得写另一类——客户端 UI 插件。本文以我自己开源的 DSH 咸鱼宠物插件为例拆开它的两层入口、槽位注入以及几个踩出来的前端工程坑背景透出、毛玻璃的包含块陷阱、穿透宿主 DOM。图服务端工具插件藏在背后供模型调用客户端 UI 插件浮在界面上给人看——后者通过shell.overlay槽位注入踩过背景透出、毛玻璃陷阱、穿透 DOM 三个坑。你每天盯着 AI 编程工具的界面八小时界面的样子却是别人定死的。想换张壁纸、加只悬浮宠物翻遍设置项找不到入口——因为这类皮肤层的改动工具厂商压根没留。DeepSeek Harness下称 dsh留了入口一切皆插件。但多数教程讲到插件都在教你怎么注册一个工具给模型调用。给界面动刀子的另一种插件——客户端 UI 插件讲的人少。本文以我自己开源的 dsh 咸鱼宠物插件为例把它从入口、槽位到几个前端坑完整拆一遍。插件跑在哪决定它是哪一类dsh 的插件分两类区别不在功能在代码跑在哪里、服务谁服务端工具插件默认形态跑在 Node 侧。它注册一个工具比如greet模型通过描述知道它存在、通过入参 schema 知道怎么调。我 8-17 那篇写的就是这种。客户端 UI 插件声明platform: web跑在浏览器里往 UI 槽位塞一个 React 组件。它不注册工具、不消费任何 Cordis 服务纯粹在界面上做文章。一句话区分前者给模型加能力后者给界面加 personality。咸鱼宠物是后者——它不替你写代码只在你写代码时陪你摸鱼。一个插件为什么要拆两层入口最反直觉的地方这个插件有两份入口文件。src/index.ts是宿主Node入口全文就一行exportfunctionapply():void{}空的。它存在只为了让 Cordis Loader 认它是个合法插件——能进 host 的 cordis.yml 插件树。它不干任何服务端的事。真正的逻辑在src/client/index.ts通过package.json的 exports 映射暴露出去{exports:{.:{default:./lib/index.js},./client:{default:./lib/client.js}},dsh:{client:{inject:[deepseek-ai/dsh-client-runtime,deepseek-ai/dsh-client-ui-slots],platform:web}}}exports[.]指向 host 那份空applyexports[./client]指向浏览器侧。dsh 的客户端运行时读到dsh.client.platform: web就知道该加载./client这份、并把它要注入的客户端服务dsh-client-runtime、dsh-client-ui-slots准备好。这个拆分的收益很实在同一个 npm 包在 Node 侧零成本空 apply 不引入 React在浏览器侧才真正加载组件。你不会因为装了个 UI 插件就给服务端进程塞进一堆前端依赖。槽位把组件挂进界面UI 不是随便往document.body一扔就完事。dsh 把界面切成若干槽位slot插件往指定槽位注册组件。咸鱼插件用的是shell.overlay——一个挂在 root 作用域的list型槽位多个插件都能往里加条目。插件先声明它需要slots服务再注入组件exportconstinject[slots]asconstdeclaremoduledeepseek-ai/dsh-client-ui-slots{interfaceSlotMap{shell.overlay:{kind:list;scope:root}}}exportfunctionapply(ctx:ClientContext){ctx.inject([slots],(scope){constdisposescope.slots.inject(shell.overlay,()scope.slots.register({name:shell.overlay,id:uiPet},SaltedFishPet),)return()dispose()})}slots.inject拿到槽位注册表slots.register把一个 React 组件绑到uiPet这个 id 上。咸鱼和壁纸选择器是两个独立的槽位条目uiPet和wallpaperPicker各自挂各自的组件。注册进 bundle一行命令背后开发时你 clone 源码、跑install.sh普通用户则是一行npx deepseek-ai/dsh plugin--profilewebaddtangyuewei/dsh-client-ui-pet这条命令本质是在持久化的 profile 目录~/.dsh/profiles/web里pnpm add这个包并把它的cordis.patch.yml追加进 bundle 清单。那个 patch 文件极简-insert:-id:ui-petname:tangyuewei/dsh-client-ui-petdsh 启动时按 bundle 顺序叠加各插件的 patch在shell.overlay里注入咸鱼。profile 目录与 npx 缓存无关重开终端、重跑 npx 依然生效——这是它比每次手动 patch 源码稳的地方。真实运行效果默认yu7毛玻璃背景与咸鱼 → 打开壁纸面板 → 切换 Porsche 718 → 置顶背景图全屏置顶→ 取消置顶 → 切换 Macan S深色系壁纸亦可选深浅色不限制。跨槽位状态两块 UI 怎么联动咸鱼和背景是两个独立槽位条目意味着它们不在同一棵 React 组件树里不能直接传 props、不能共享 context。但点一下隐藏咸鱼要把背景也收起来这个联动必须成立。解法是抽一个模块级共享 storevisibility.tslethiddenfalseconstlistenersnewSet()void()exportfunctionsetPetHidden(next:boolean):void{if(hiddennext)returnhiddennextfor(constlisteneroflisteners)listener()}exportfunctionsubscribePetHidden(listener:()void){listeners.add(listener)return()listeners.delete(listener)}一个布尔 一个监听器集合。宠物按钮调用setPetHidden背景模块的订阅者收到通知后同步收起。宠物组件本身用useSyncExternalStore接这个 store所以即使槽位被重新挂载读到的仍是同一份持久化的可见状态不会宠物没了、背景还在。代价我说在前面召唤按钮目前靠查找页面上含 “Session log” 文字的按钮来定位自己Shell 结构一变就可能失效。这是已知限制不是特性。壁纸 API丢图即生效壁纸不区分深浅主题用户随便挑。源图丢进src/client/wallpapers/构建脚本build-wallpapers.mjs自动缩放到 1920px、base64 编码进bg-images.generated.tsgitignored每次构建重生于是加一张壁纸等于丢一个文件不用手敲 base64。选择态用localStoragekeydsh-ui-pet.wallpaper持久化并通过window上的CustomEvent广播exportfunctionsetCurrentWallpaperId(id:string):void{localStorage.setItem(STORAGE_KEY,id)window.dispatchEvent(newCustomEvent(CHANGE_EVENT,{detail:{id}}))}背景模块和选择器都订阅这个事件换壁纸时两边同时刷新刷新和重启后选择还在。几个前端坑架构落地的代价插件能跑起来靠几处对症的前端技巧。它们不性感但少一个界面就废。背景透出。主题背景默认不透明会盖住 body 上的壁纸。解法是把主题背景底色强行透明让 body 的壁纸透出来body.dsh-bg-glow, body.dsh-bg-glow #root, body.dsh-bg-glow #root *{--dsw-alias-bg-base:transparent!important;}一个 CSS 变量加!important覆盖掉主题对background简写设的不透明底色。毛玻璃的包含块陷阱。侧栏想做毛玻璃直觉是给列加backdrop-filter。但backdrop-filter会让那个元素变成其内部position: fixed后代的包含块——设置面板是 portal 渲染的会被困在 280px 宽的列里。改用isolation: isolate也不行它会创建层叠上下文Chrome 会裁剪上下文内fixed后代到自身overflow。正解是滤镜放::before伪元素、z-index: -1逃逸到 body 背景层body.dsh-bg-glow [class$sidebarCol]::before{content:;position:absolute;inset:0;z-index:-1;-webkit-backdrop-filter:blur(26px)saturate(160%);backdrop-filter:blur(26px)saturate(160%);}这俩雷在 MDN 的backdrop-filter条目里都有记载但只有真写一遍才知道它会咬人。穿透宿主 DOM。宿主的列用了 CSS Modules类名是带哈希的sidebarCol__abc123。我没法 import 宿主组件去加样式只能用后缀属性选择器命中本地名body.dsh-bg-glow [class$sidebarCol]{background:rgba(255,255,255,0.55)!important;}[class$sidebarCol]匹配以 sidebarCol 结尾的类名不依赖哈希前缀。代价是选择器写得宽未来宿主改本地名规则就得跟着改。主题与光晕。MutationObserver监听body的data-ds-dark-theme属性切深浅色鼠标跟随光晕只往--bg-mx/--bg-my两个 CSS 变量写坐标由合成器compositor处理不触发重绘——mousemove用passive监听不阻塞滚动。已知限制收益和代价一起给这个插件不完美我把它写进 README 的已知限制零持久化宠物位置、饱腹度、心情都是会话级内存刷新即重置无配置面板所有参数衰减速度、边距、尺寸要改源码重新构建没暴露给用户召唤按钮依赖 DOM 查找Shell 结构变更可能失效壁纸内嵌 base64无外部请求想动态加载得自己改。这些不是 bug 清单是纯前端展示插件这个定位下的取舍。它本来就不打算做服务端、不做账号体系、不做云同步。收尾写这个插件最大的收获不是多了只咸鱼而是被迫把 dsh 的宿主/浏览器边界、槽位契约、宿主 DOM 穿透摸清了一遍。插件化的另一种形态就在这它不只想给模型加能力也想给界面加 personality。当 AI 编程工具成为你每天盯八小时的东西能不能把它改顺手本身就是工程问题。客户端 UI 插件是这块拼图里被讲得最少的一块——希望这篇把它补上。参考资料开源仓库 tangyuewei/dsh-client-ui-pet2026 年 8 月。MDN Web Docsbackdrop-filter。作者唐悦玮 | 从后端出发用 AI 拓展到全栈的工程师。

相关新闻

2026/8/22 17:15:50

三步把 NCM 转成 MP3:ncmdump 免费本地无损转换教程

三步把 NCM 转成 MP3:ncmdump 免费本地无损转换教程 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 车载 U 盘里塞满网易云下载的 .ncm 文件,插上车却一个都放不了?问题不在歌,在格式。…

2026/8/22 18:25:54

基于多智能体LLM框架的时序异常检测:从原理到工程实践

1. 从直觉到系统:为什么我们需要“专家级”时序异常检测?在数据驱动的世界里,时间序列数据无处不在:服务器的CPU负载曲线、工厂设备的振动传感器读数、金融市场的股价波动、城市交通的实时流量……这些数据流里,偶尔会…

2026/8/22 18:25:54

水下搜索建模:从声呐物理到贝叶斯路径优化

1. 美赛B题的真实战场:不是写代码,而是解构“搜索”这个动词2024年美国大学生数学建模竞赛(MCM/ICM)B题标题直白得近乎挑衅:Searching for Submersibles——搜索潜水器。没有炫技的术语堆砌,没有模糊的隐喻…

2026/8/22 18:25:54

FSearch 完全上手:让 Linux 文件搜索快到秒回

FSearch 完全上手:让 Linux 文件搜索快到秒回 【免费下载链接】fsearch A fast file search utility for Unix-like systems based on GTK3 项目地址: https://gitcode.com/gh_mirrors/fs/fsearch 你肯定遇到过这种场面:上个月存的一份合同&#…

2026/8/22 18:25:54

数字孪生与多尺度规划:智能体如何重塑自动化事件响应

1. 项目概述:当数字孪生遇上多尺度规划,智能体如何重塑事件响应最近和几个做安全运维和工业自动化的朋友聊天,大家不约而同地提到了一个痛点:面对复杂系统里突如其来的故障或安全事件,传统的响应流程就像在迷宫里摸黑找…

2026/8/22 18:20:54

在东莞找医疗推车气动杆解决方案,哪家产品品质更靠谱?

最近不少做医疗设备的朋友找我问,在东莞找医疗推车用的气动杆,东莞大大小小做气弹簧的那么多,挑花了眼,到底哪家更靠谱?我接触这个行业快十年了:给大家捋捋清楚,帮你少踩坑。医疗推车的气动杆&a…

2026/8/21 13:13:49

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/21 20:14:07

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/21 15:40:01

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/21 15:40:01

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/22 1:39:53

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…