description: "右侧实时组件栏:会话、机器、额度与成本,一眼看完。"
33.6/100
综合分
5
GitHub 星标
568
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-widgetsnpm 源 · dsh-widgets 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · ui
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 1 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-widgets @ 1.8.5
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/24 19:33:31
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 12 处,展示前 12 条)
- 发起网络请求lib/index.js:39
const upstream = await fetch(USAGE_URL, { headers: { Authorization: `Bearer ${key}` } }); - 发起网络请求lib/index.js:64
entry.data = await (await fetch(USAGE_URL, { headers: { Authorization: `Bearer ${key}` } })).json().catch(() => null); - 发起网络请求lib/index.js:320
const upstream = await fetch(COMMANDCODE_ENDPOINTS[name], { - 发起网络请求lib/index.js:697
const res = await fetch(url, { - 发起网络请求lib/index.js:1372
upstream = await fetch(DEEPSEEK_BALANCE_URL, { - 读取本地文件lib/index.js:1548
const entries = await readdir(sessions, { - 读取本地文件lib/index.js:1559
const s = await stat(full); - 写入本地文件lib/index.js:1842
writeFileSync(file, PS_SCRIPT, "ascii"); - 执行外部命令固定命令lib/index.js:1854
execFile("powershell.exe", [ - 读取本地文件lib/index.js:2216
const parsed = JSON.parse(readFileSync(path, "utf8")); - 读取本地文件lib/index.js:2227
modifiedAt: statSync(path).mtimeMs - 读取本地文件lib/index.js:2250
mtime = statSync(tablePath()).mtimeMs;
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-client-ui-primitives给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
4 张 · 点击查看大图功能亮点
- 右侧实时组件栏
- 展示会话机器额度成本
- 可复用小组件
DeepSeek Harness 的模块化小组件系统——可复用小组件、自适应布局,加一套统一的设计语法。
最近更新
在 GitHub 查看全部 →v1.8.52026-10-14内容较长已截断
· v1.8.5 — 又有五张卡片上线,一份数据源契约,以及一条会读取同住者状态的侧栏
卡片库开放以来最大的一次内容更新:六个新单元——时钟、绑定 DSH 自有日程服务的日历、任务计时器、秒表、倒计时和 DeepSeek 官方余额卡——把注册表从 55 推到 61,每张卡片背后的数据都迁到一份声明式契约上(单元在 manifest.json 里声明数据源,一个通用轮询器替所有单元取数)。底下还压着主人在同一两周内踩到的四个缺陷:同住者把侧栏往下推之后,悬停判定整整错了一行(45 项检查挂了 25 项);shell 一换节点,会话头部锚点就冻住;整卡规则和滚轮上限是盲着排的,因为 paneH 压根没测过;nvidia-smi 的 MiB 被当成字节传了出去。随它们一起来的还有两处面向构建的补充:一套离线门禁(node scripts/gates.mjs,G1–G12)接进了新的 CI 工作流,以及下面两次评审事故催生的卡片标题/正文姿态标准。按主人拍板以补丁形式发布,和 v1.8.1 一样:内容是功能级的,版本号不是重点。
· 新增 — 时间家族:时钟、日历、任务计时器、秒表、倒计时
· 五个单元排在 31–35 号,每个都走生产工作流构建,并按单元归档在 docs/workflow/records/2026-10-14-*.json(四份记录都停在 INTEGRATION,各自经历 2–4 轮返工):
- clock(2×2)——指针或数字表盘,六个时区,秒针真的会走;
- calendar(2×2 / 2×4)——本月视图,今天带标记,周末或法定假日着色,带 DSH 计划任务的日期打点;2×4 列出即将到来的任务。它声明 reads: ["schedule"],在没有日程子系统的部署上退回普通月历网格;
- task-timer(2×2 / 2×4)——本轮跑了多久、此刻在等什么(工具调用还是模型步骤),读会话已有的投影,轮次开着时每秒推进;
- stopwatch(2×2)——开始/暂停/继续/重置,读数自己走,状态存在实例配置里,刷新或换个浏览器都还在;
- countdown(2×2)——时长胶囊(1/3/5/10 分钟,其中一个可自定义)或下一个 DSH 计划任务。
· 返工那几轮才是精彩的部分(下面两条标准就是从它们写出来的):倒计时首发时没写 reads: ["schedule"],done 字段还是死的;日历把议程画反了方向,when 列一直截断,直到给了它 64px;秒表第一版表盘重做成 HH:MM:SS.cc 加圆形控件;任务计时器把工具计数器叫成“calls”,改名 tools 后才对,2×2 的副行也挪到了并行调用数上。
· 共用机制,不是五份拷贝:src/client/lib/clock-face.ts + timer-state.ts,富渲染器 src/client/render/rich/{clock-face,month-grid,timer-face}.tsx,一个 CardBody 富块和侧栏的计时器分发。加这五个单元,除了这些文件加五个单元目录,别处一行没动。
· 单元在上游发现的问题写下来而不是绕过去(docs/research/TIME-CARDS-INTEGRATION.md,对照 0.2.0-rc.2):schedule.create 没有 @Remote 面,卡片建不了提醒(能列/编目/查历史/更新/删除);todos 没有 id 也没有日期,turn/start 时被清空;jobsBySession 不存在——真实 API 是 ctx.jobs——所以这个部署里 jobs 卡的分会话数字永远是 null;渲染器自己的 tick 是“会动”读数唯一能待的地方。
· 新增 — DeepSeek 余额,以及它读的那条宿主路由
· deepseek-balance(2×2,68 号,分组 deepseek,source: deepseek)显示官方账户余额,旁边是账本推算出的当日花费。
· 它是第一个需要自己宿主路由的单元:src/host/deepseek.ts 发布 /api/deepseek-balance(十个字段——available / currency / day / granted / hasBalance / sampledAt / spentToday / toppedUp / total),路由表、客户端契约和桥快照(bridge.deepseekBalance 及其错误兄弟)都跟着扩了。
· 推算日离线折叠,对着构建产物验:scripts/verify-deepseek-balance.mjs 对账本折叠和路由能回的所有错误码跑 17 条断言
v1.8.42026-10-06内容较长已截断
针对 v1.8.3 之后仍存在的三类投诉,这次发的是以修复为主的补丁:外壳重建框架后,吞掉事件的逻辑悄悄失效了;折叠动画在每一帧都同步强制布局;右侧栏开关时,组件明显缩小又长回去。前两类看不见——动画曲线是对的,帧率也没掉——只能在事件日志和 DevTools 的 trace 里看到;第三类看得见,但找不到原因。之后又并进来第四类:组件区未激活时切换工作区会话,会把整个区域画到会话上。共十八处改动,全在插件内部——外壳没动。
关于「会话区滑动时卡顿」:这次发布把侧栏开关中插件自己那部分开销压到采样 CPU 的 4.9%(V8 profile,三次真实开关),trace 里最热的五个 JS 帧现在全是官方外壳自己的(ReactDOM 的 commit 阶段和调度器的 work loop)。那部分在外壳边界之外,所以这次发布不声称修好了它——见下方「已验证」里的「未声称」。
· 已修复——外壳重建框架后,让步节拍就死了
· 监听器绑在了一个已经不存在的元素上。侧栏「与网格过渡同一节拍同步让步」这条路径监听 AppFrame 元素上的 transitionrun,缓存在模块级的 frameEl 里,只在右栏列断开时才重新绑定。外壳换掉框架节点却没断开那一列时,旧监听器就挂在一个脱离文档的节点上:transitionrun(grid-template-columns) 永远到不了我们这儿,节拍于是悄悄退化成 240 ms + 520 ms 的轮询兜底——这正是「能用,但重载几次后就不灵了」的反馈。现在绑定改成 document 上的单个捕获阶段监听器,按类名后缀(_frame)匹配框架,不再依赖一个存活的引用(src/client/rail/measure.ts,install() / disposer)。用 scripts/diag-yield-beat.cjs --rounds 4 跨四个重载周期实测:节拍延迟 1 ms / 0 ms / 0 ms / 0 ms,预算 120 ms,对照组报告 transitionrun=null(事件确实在我们上层被吞了,兜底确实就是以前在干活的那个)。
· data-yielded 每帧都在写。updateRailBudget 每次执行都无条件设置该属性;dsh-ui-harmonizer 用 MutationObserver 观察那个抽屉,任何属性变化都会重跑 refreshMaterial(每次调用 12–27 ms,折叠 trace 里调了 10 次)。现在只在值真正翻转时才写。
· 一次 :root 变量写入夹在两次布局读取之间。measureRailTop 还没读完自己的矩形就写了 --dsx-rail-top;在 :root 上写自定义属性会让整个文档的样式失效,于是之后每次读取都变成全文档强制重排——这是插件自己造成的最大的单一布局来源。该函数现在改成先读后写(// ── READS FIRST, WRITES LAST ──),四处写入全部归到函数末尾,节流分支会重新发布 --dsx-rightbar-w,让延迟的重新探测不会漏掉宽度变化。
· 已修复——折叠动画每帧都在强制布局和样式
· syncWholeCards 读了 rail.scrollTop 和 rail.clientHeight。它在每次 useLayoutEffect 执行时重新测量侧栏来算可见区间,等于在所有者持续运行的那一个动画上每帧做一次同步布局。现在它直接取参数,用组件已持有的值算区间;该 effect 的依赖数组原本基于对象标识,而 wave-geometry.ts 每次渲染都会重建 staticLayout,所以换成了数值指纹守卫。trace 证据,改前 → 改后(--what=fold,有头模式 1707×1067 @ DSF 1.5):bandOf ← syncWholeCards 从 9 次强制布局 + 14 次强制样式降到零。剩下的强制布局来源都在本插件之外(内联 check/raf、外壳 React 合并块:21638、dsh-ui-harmonizer 自己的 refreshMaterial、产品绘制的命中目标)。
· 十三张卡片各自让自己的样式失效。CardBody 的 useLayoutEffect 每次执行都调用 setAttribute / removeAttribute 设置 data-dsx-overflow,不管溢出状态有没有变;任何属性变更都会让该节点的样式失效,于是一次折叠多出 13 次样式重算(effect 自身耗时 44.2 ms)。现在用 ref 守卫,只在翻转时才碰 DOM。
· 三个侧栏变量每帧写入且不检查值是否相等,其中一个(--dsx-rail-
v1.8.32026-09-30
· v1.8.3 — 在 DSH 0.2 上侧边栏又吞掉了组件栏,插件列表终于能看到我们了
以修复为主的补丁版,同时把仓库里一直未发布的 DSH 0.2 适配一并送出:组件栏按 0.2 的层叠与轨道语义重新对齐,OpenCode 数据源只在有东西渲染它时才拉取,插件在 DSH 自己的插件列表里终于有了图标、语言包和简短说明。
· 已修复 — 在 DSH 0.2 上右侧边栏不再吞掉组件栏
· 根因是轨道语法。DSH 0.2 把 AppFrame 的内联轨道从 280px minmax(0px, 1fr) 720px 改成 … minmax(0px, 864px),而组件栏读取「轨道要去的宽度」时只接受纯 px 的末位 token。于是 readTargetColumnWidth 和 readTargetRightbarWidth 都返回 null,predictRailBudget 也跟着失效,让位退化成 240 ms + 520 ms 的稳定防抖:组件栏先跳进腾出的对话列,约 500 ms 后又缩回面板下面。在 1920×1080 下实测,按一次「打开右侧边栏」:改前,网格在一帧内从 1640px 变成 776px,且整帧没有触发任何 transitionrun/start/end;改后,11–13 个中间宽度逐帧插值,事件全都在,组件栏右边缘全程钉在 1920(scripts/verify-swallow-20.cjs,9 条断言)。
· 设置抽屉(组件区域)也一起被吞。它的 right 声明被 panel.module.css 覆盖(同优先级、后导入),用的是原始 --dsx-rightbar-w,抽屉因此不再跟随组件栏;又因为它被 portal 到(z-index 30),浮在官方 0.2 右列之上,而后者根本没有 z-index。现在它跟随同一个变量,并把「吞掉」表达成动效——用和侧边栏相同的 0.3 s 曲线从右边缘滑出。
· 已变更 — OpenCode 数据源改为按需拉取
· 没有组件,就不发请求。采集器以前无条件拉取所有数据源:在没装 OpenCode 组件、密钥也删掉的机器上,/api/opencode-usage 每次重新拉取都返回 503,浏览器控制台因此一直为用户从没配过的功能留一条红线。现在 hasConsumer(source) 会检查已安装组件声明的 source(usage / cc / sys / github),只请求确实有东西渲染的数据;单键路由不再单独请求(池路由始终 200,且已带上主键的载荷)。
· 宿主不再用 5xx 说「未配置」:503 { error } 改成 200 { configured: false }——可选键缺失是预期状态,不该让每个客户端都记一条错误。
· 已新增 — 插件列表里的图标、名称和中文说明
· icon.svg:36×36 viewBox,蓝→靛线性渐变加扁平填充几何,按官方留白(内容占图块 50–60%,与内置实验包一致),配色沿用智能体团队(青色 #45D9E7、琥珀 #F2AF63、主蓝渐变、浅蓝 #7CB7FF)。
· locale/zh.json + locale/en.json(meta.title = 组件系统),配 exports["./locale/*.json"],再加 README front-matter 作为第二通道;package.json.description 现在是一句简短中文——它是唯一对 link: 安装的插件也生效的文本通道。
· 浅色和深色 App 图标(docs/icon/app-icon-{light,dark}.svg)。
· 已变更 — 随本版送出的 DSH 0.2 适配(此前未发布)
· 放大浮层改为纵向裁切,滚动时卡片不再画到会话头部和顶栏上;回合导航保留产品自带的 z-index 7(去掉 9 的覆盖);输入框停靠区的信息栏不再被隐藏。
· 早先的卡片和设置/配置页重构在本版首次送出。
---
以修复为主的补丁版,同时把仓库里一直未发布的 DSH 0.2 适配一并送出:组件栏按 0.2 的层叠与轨道语义重新对齐,OpenCode 数据源改为按需拉取,插件在 DSH 自己的插件列表里终于有了图标、名称与简短的中文说明。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成description: "右侧实时组件栏:会话、机器、额度与成本,一眼看完。"
English · 简体中文
DeepSeek-Harness Widgets · 组件栏
对话右边那条空带,变成一格格实时卡片;想收起来,一键就收起。
61 张卡:会话 · 上下文 · 后台任务 · 这台机器 · 额度与花费 · 峰谷定价每张独立安装、2×2 / 2×4 调尺寸、按实例调阈值、不看就关;指针划过有 macOS 坞站式放大波峰。
装好之后的样子(左边的静帧点开是动效:rail-demo.gif,3.8 MB)· 以及安装、调尺寸、关掉每一张卡的组件市场。
安装(30 秒):把下面两行整段复制给你的 DSH agent,或者自己在终端跑:
web 是 dsh web 的默认 profile 名;你自己的 profile 名 = $DSH_HOME/profiles/ 下的目录名
dsh plugin --profile web add dsh-widgets
装完重启 dsh web,浏览器硬刷新一次(Ctrl+Shift+R)
不会用命令也没关系——跟你的 DSH 说一句「装上 dsh-widgets 插件,重启 web 之后告诉我」就行;装完点会话头部的 组件 图标,栏就出来了。
展开完整文档(README 剩余 1 节)
description: "右侧实时组件栏:会话、机器、额度与成本,一眼看完。"
English · 简体中文
DeepSeek-Harness Widgets · 组件栏
对话右边那条空带,变成一格格实时卡片;想收起来,一键就收起。
61 张卡:会话 · 上下文 · 后台任务 · 这台机器 · 额度与花费 · 峰谷定价每张独立安装、2×2 / 2×4 调尺寸、按实例调阈值、不看就关;指针划过有 macOS 坞站式放大波峰。
装好之后的样子(左边的静帧点开是动效:rail-demo.gif,3.8 MB)· 以及安装、调尺寸、关掉每一张卡的组件市场。
安装(30 秒):把下面两行整段复制给你的 DSH agent,或者自己在终端跑:
web 是 dsh web 的默认 profile 名;你自己的 profile 名 = $DSH_HOME/profiles/ 下的目录名
dsh plugin --profile web add dsh-widgets
装完重启 dsh web,浏览器硬刷新一次(Ctrl+Shift+R)
不会用命令也没关系——跟你的 DSH 说一句「装上 dsh-widgets 插件,重启 web 之后告诉我」就行;装完点会话头部的 组件 图标,栏就出来了。
支持 DeepSeek Harness 0.1.0-rc.6+(0.1.x)与 0.2.0-rc.1+(0.2.x)。组件栏挂在会话头部的 组件 图标后面;在 设置 → 组件 里安装、排序与调整尺寸。
你得到的:61 张卡,装的、摆的、调的都随你——
- 实时会话——轮次·步数、上下文水位与压缩、token 与缓存命中率、LLM 时长、首 token 延迟、工具调用、后台任务与子代理;
- 钱与额度——OpenCode Go 的滚动 / 周 / 月窗口;Command Code 的 5 小时 / 周 / 月限额,点一下即在整个 Key 池之间切换,外加余额、月末外推与今日推荐;
- 时间,以及官方钱包——时钟(指针或数字、六个时区)、日历(有 DSH 定时任务的日子打点;2×2 月历,2×4 多一列临近日程)、任务计时器(本回合已跑多久、此刻在等什么)、秒表(开始 / 暂停 / 复位)、倒计时(时长胶囊或最近的定时任务),以及 DeepSeek 余额卡(旁边是账本推导的当日花费);
- 这台机器——CPU / GPU / 内存 / 磁盘 / 网络 / 供电,含迷你折线、环形图,以及本地服务与代理出口看板;
- 你自己的视图——每张卡都是独立单元:装它、选 2×2 或 2×4、改它的阈值,或者干脆关掉;其余由组件栏自行排布。
一句话:装一个插件,对话右侧那条空带就变成一格格实时卡片——token 与缓存、上下文、后台任务、机器状态、额度与花费。每张卡都是独立单元,装、改尺寸、调阈值、关掉都随你;整条组件栏想收就收。
DeepSeek-Harness Widgets 会在 DeepSeek Harness 会话页的右侧放上一条组件栏。每张卡都是独立单元:从内置市场里挑,放进 1–4 列的网格,选 2×2 或 2×4,按实例单独配置,不看的直接关掉。组件栏只占用正文旁边那条空带,一键即可整栏收起。
为什么值得装:那些数字本来要另开标签页或另开面板才看得到——上下文还剩多少、这个会话花了多少、额度还有几天到期、机器烫不烫。它们更适合安静地待在对话旁边,扫一眼就走,不打断你正在做的事。
如果它帮上了忙:这个插件没有别的分发渠道,只有「被人看到」这一条——在 Physicolor/dsh-widgets 点一个 star,就是把它推到 DSH 各插件目录前列、让更多人看到的那一下(GitHub 的搜索与推荐都按 star 排序)。从视频过来的话,在弹幕或评论里说一句你装了哪几张卡,比 star 更能让我知道该往哪加;Bug 与组件想法在 issues 里一直开着。
官网 / Showcase
项目官网位于 website/,线上地址 https://physicolor.github.io/dsh-widgets/,是一座设计系统站点:讲 dsh-widgets 是什么、为什么存在、全部 61 个真实组件,以及 DSH Widget Design Grammar(直接取自 src/client/index.ts 的真实单位/间距/放大公式,并带一条交互式组件栏跑插件自己的放大曲线与右对齐回流)、Widget Anatomy(一张真实卡片放大 ×2,每一处内边距、间距与内缩都从 DOM 实测标注)、DSH Visual Audit(13 条明示规则对全部 61 个组件按真实 getBoundingClientRect 测量打分——规则驱动,不是模型打分)、部件单元化架构、生产 Workflow,以及「需求表 → Widget Specification」生成器。站点为纯 HTML/CSS/JS,无构建步骤,所有路径都是相对路径(适配项目 Pages 的 base path)。node website/verify.mjs 自校验(静态 + SEO + Edge 无头浏览器检查,共 88 项);组件表、静态画廊 markup 与 JSON-LD ItemList 由 node website/gen-site.mjs 从各 manifest 生成(--check 在漂移时报错);node website/gen-og.mjs 重新生成社交分享图。部署方式见 website/README.md。
当前功能
多列网格布局
| 项目 | 说明 |
| 最多列数 | 1 / 2 / 3 / 4(设置中下拉选择,默认 2 列)。这是上限:空间不足时自动逐级回退,空间充裕也不会超过它 |
| 最多行数 | 1–12(默认 5 行)。与「最多列数」相乘就是组件区一次能摆下的格子数,也就是可放置组件的上限:「最多组件数」按 列 × 行 生效(存储值不会丢,调大列或行就立刻恢复),设置行上有感叹号标记,悬停说明是被哪一个数字覆盖的 |
| 可用宽度从哪来(为什么还有空位却不加列) | 组件栏只吃正文阅读宽度之外的余量:列宽 − 阅读宽度 − 74px − 24px(74 = 官方滚动区两侧内边距与列宽拖拽带的保留,24 = 安全余量)。阅读宽度是官方发布的 CSS 变量——DSH 0.1 叫 --dsh-chat-content-width,0.2 改名成了 --dsh-chat-user-width;插件此前只读旧名,读不到就退回估算值(1578px 窗口下 831px 而不是真实的 748px),预算少算约 83px,于是「对话区两侧明明有空间却始终少一列」。两个名字现在都读。修正后同一窗口实测:列宽 1298、阅读宽度 748 → 可用约 452px,3 列 ×130px(占用 430px)或 4 列 ×100px;正文卡片保持 748px 不被压缩。左右两条空白里只有右边的可被占用(组件栏右锚定,靠给滚动区加 padding-right 占地,正文会重新居中;占得超过 列宽 − 阅读宽度 就会压小阅读宽度,这条线不能越),左边那条是正文居中的留白 |
| 2×4 长方形组件 | 宽度为两个 2×2 加一个间距,高度与 2×2 相同;同一组件可同时以两种尺寸安装 |
| 无空隙排列 | 组件按最优适配(best-fit)打包,2×4 留下的空格由后续 2×2 回填,拖动排序永不留下空洞。3 列时若 2×4 会被「截断」,它整体前移一格、被顶掉的 2×2 后移一格(类似四舍五入),长方块不会被孤立 |
| 拖宽实时响应 | 拖动对话宽度时每帧重算可用宽度,卡片在该列数下的档位边界处立即换档(拖动期间正文内边距不做补间,与对话区同步);列数在阈值处增减。全部为 2×4 时降级跳过 3 列(2 ⇄ 4);显式选择 3 列则始终保留 |
| 过渡手感 | 尺寸换档与列数变化都用轻微回弹(约 4% 过冲的弹簧曲线)平滑放大/缩小与滑到新格子,另加一段按排布顺序错开的微型沉降波(约 1.6% 幅度、逐卡 30ms)——不是整块组件区一起「弹」。组件栏自身的宽度与对话区留白也用同一条弹簧,所以换档时容器、卡片、正文三者同步移动,不会出现「容器已经跳过去、卡片还在追」 |
| 悬浮放大 | 多列网格同样生效;放大时行/列都按平面距离让位,间距恒定 |
| 滚轮 = 整行挡位滚动 | 滚轮以整行为挡位(位移恒为 行高 = 卡片边长 + 间距 的整数倍),因此顶部那行永远完整、不会被截断;一档把下一行拉到顶行的位置。底部被视口切掉的那张卡直接不画(见下方「只显示完整组件」),所以组件栏里永远只有完整卡片。滚动一律走浏览器自身的平滑滚动,鼠标在卡片上、间隙里还是组件栏空白处都一样顺滑,不会出现逐格硬跳 |
组件栏的顶边贴在会话页头的下沿(--dsx-rail-top,首行卡片正好压在这条线上;1578×950 实测 44px,首行卡片 50px)。锚点取「组件按钮(页头里的胶囊)所在那一行」与「页头下沿」两者中更低的一个:胶囊是用户读组件栏位置的参照物(实测它在距窗口顶 11px 处),但页头本身是不透明白底 + z-index: 21,而组件栏所在的 composer seat 只有 7 —— 画在页头下沿之上的卡片是被盖住而不是被裁剪(实测把栏顶放到胶囊的 11px,首行会被盖掉 33px)。所以默认值就是「贴到胶囊那一行所允许的最高位置」。
连续波峰悬浮放大
macOS Dock 式悬浮放大,两种跟随模式(在 设置 → 组件 → 外观与布局 → 悬浮放大 → 连续跟随放大 中切换):
- 无极变化(连续跟随):真正无极——每张卡片的缩放由它自身到指针的连续欧氏距离决定,指针任意移动,波峰都在卡片之间平滑滑动。它每一帧都落位到稳态的右对齐几何(transition: none),因此卡片右缘在移动过程中也始终与组件栏齐平,不会出现宽度/右缘失步。
- 离散(默认):复用同一套连续几何,只把指针吸附到量化格点(行/列中心 + 相邻中点:Y 方向 2·行数−1 个点、X 方向 2·列数−1 个点),波峰在格点之间滑动同样由那条弹簧驱动。
波动的进度是一条每帧写入的弹簧:displayed = 1 + (target − 1) · p。target 是指针的实时几何,p 在静息(0)与激活(1)之间按 Apple 参数化曲线取值(response 200ms、bounce 0.1——约 250ms 收敛、约 0.15% 过冲 ≈0.4px)。两者都连续,因此进入、跟随、离开在两个方向上都是一条不中断的位移;指针继续移动只会改变 target,永远不会让曲线重来(此前两种写法——朴素的 CSS transition、以及冻结目标——都正是败在这里,现已全部删除)。离开用同一公式反着跑,prefers-reduced-motion 下一帧到位。
两种模式下,放大后的 deck 都由固定悬浮层绘制在组件栏滚动裁剪盒之外,因此向左放大不会被裁剪,而静息时的组件栏宽度(以及对话列距离)始终不变。缩放保持正方形卡片与恒定间距;放大倍数可在设置中调节(1.0–1.4)。
进出悬浮是一次连续位移,不是两层叠图的交叉淡入:悬浮层在静息时与真实卡片逐像素重合,因此切换发生在几何完全一致的瞬间(不可见),随后约 250ms 内把每张卡片的 top/right/width/height 与缩放一起、按同一条弹簧写入波动位置;离开时反向走回静息位置后才交换回来。被悬浮的那张卡在放大层里也保留品牌蓝描边。
进入剩下的成本是合成层成本、不是重绘成本,修法是一个常驻提示:放大层的 15 个槽位(以及底部那颗设置齿轮)始终带 will-change: transform,合成层在指针到达之前就已存在——把提示挂在 hover 上实测:自然进入的首帧是 63ms 的层提升 + 栅格化帧;改成常驻后同一进入是 16ms、零掉帧。过去在组件栏空闲时把放大层翻出来 64ms 的「栅格预热」已删除:它几乎从不与真实悬浮重合,而常驻提示一到位,那次卡顿就再没复现。
组件栏与放大层由同一个父级统一接管指针(两者是必须的兄弟节点:放大层要逃出组件栏的滚动裁剪盒),而且这个指针面是几何连续的:波动激活时放大层自身可命中、并把命中盒按「卡片向左越出的最大量」向左扩宽(左内边距同步补偿,卡片不动),所以卡片之间、卡片与间隙之间、以及放大卡片越出组件栏左缘的那一条都不会掉出指针面——否则间隙的命中会落到后面的对话上,导致「悬浮到间隙就取消悬浮」和「在边缘来回移动时反复放大缩小」。指针真正离开时才结束波动(离开判定还带 6px 容差防抖)。在放大卡片上滚动滚轮依然滚动组件栏(放大层在滚动容器之外,滚轮被接管为整行挡位滚动并阻止穿透到后面的对话)。
放大层是挂到 <body> 上的(2026-10-01):它要压过 shell.overlay(z-index 20,harness-ui-harmonizer 的居中卡片外框就在这一层),而组件栏所在的 composer seat 只有 7 —— 同一个堆叠上下文里再高的 z-index 也越不过去。挂到 body 之后放大层以 z-index: 26 参与根上下文的排序:在官方右侧面板(10)、shell overlay(20)之上,在设置面板(30)之下。随之而来的三件事都处理了:指针焦点改由 window 级 mousemove 驱动(放大层已经不在那个 wrapper 子树里了,否则指针落在放大卡片上时波动会僵住)、滚轮监听同时挂在放大层上(它激活时自己的盒子盖住整条组件栏,滚轮会打到它而不是组件栏)、以及「设置」格子的显示类同时挂在放大层上(它的镜像按钮也在 body 下)。
加载骨架
外部数据(OpenCode 用量 / Command Code / 系统监控)尚未返回时,卡片不再显示空值或 0,而是画出它正在等待的那块内容的外形(标题保留真实组件名,占位块为圆角填充物,带 1.5s 微光扫过,prefers-reduced-motion 下静止)。外形按组件定制,因此加载中的 rail 依然能看出是哪些卡:三个环形图画三个圆角方块,柱状图/折线图画一个大圆角矩形,热度图画一个宽块,数字行画它那几列短块,额度卡画三条堆叠的条;纯文字卡保留数值胶囊 + 正文行。数据返回后原地替换,卡片尺寸与位置都不跳动。
连续曲率圆角
卡片圆角是超椭圆(corner-shape: squircle),不是圆弧:曲率从直边连续过渡进来,这正是 iOS / Figma 式圆角矩形看起来「软」而不是「切」的原因。它是原生 CSS,所以卡片描边与 box-shadow 都沿同一条轮廓(用 clip-path 做超椭圆会把两者都切掉);不支持 corner-shape 的引擎退化为普通圆角,其他行为不变。
两个控件都在 设置 → 组件 → 外观与布局:开关(默认开,留着是为了能对比两种形状)与圆角档位——半径占卡片短边的比例(12 / 16 / 20 / 24 %,默认 16%)。用百分比而非像素,因为参照物保持的是圆角与边长的比例:150px 的卡片与放大到 190px 的副本不该拿到同样的圆角(12% 正好还原旧的固定 16px,边长 150 时)。该档位同时驱动组件栏的静息卡与放大卡、组件市场 / 组件配置的预览、以及加载骨架。
设置页的分组与控件规则
「外观与布局」tab 按作用面分组,而不是按「哪次加进来的」排成一条长列表:
| 分组 | 内容 |
| 网格与尺寸 | 最多列数 / 最多行数 / 最多组件数 / 卡片基准边长 / 组件间距 / 只显示完整组件 |
| 卡片外观 | 连续曲率圆角 / 圆角档位 |
| 悬浮放大 | 连续跟随放大 / 悬浮放大倍数 |
| 打开与收起动画 | 展开方式 / 打开起始缩放 / 位移曲线 / 回弹幅度 / 缩放曲线 |
| 与宿主界面 | 隐藏输入框下方文字条 |
页脚另有一个「恢复默认设置」——两次点击生效,只回滚本页的外观/布局,不动已装组件、排序与每卡配置。
「最多列数 / 最多行数 / 最多组件数」三项相邻且都是数字选择器,因为第三项就是前两项的乘积(被覆盖时该行出现感叹号,悬停说明被哪个数字限制)。控件选择只有一条规则:
| 值 | 控件 |
| 数值区间 | Stepper(产品自带的数字控件) |
| 精选离散集合 | Select(产品菜单范式;本页只剩「圆角档位」这一处) |
| 布尔 | Switch |
这条规则被破坏过一次:最多列数 早期用 Select 实现,而后来加的 最多行数 用的是 Stepper——同类值两种控件。node scripts/verify-rail-rework.cjs 现在会逐行审计(列/行必须是 stepper、全页只允许一个 Select、并断言分组顺序),所以新增设置行要先选分组再插入,不存在「往末尾追加」。
打开/收起动画与「只显示完整组件」
「整体缩放」下组件栏从右上角锚点出现:既向右滑入(和官方右侧面板同一条 0.3s 过渡),又从起始缩放长回 100%。这两半是两个元素、两条曲线:外层 translateX(位移) 走「位移曲线」,内层 scale(s0) 走「缩放曲线」(一个 transform 只能带一条时间函数,所以拆成两层;两层都是 inset:0 的 fixed 盒子,组件栏的坐标因此完全不变),两者共用 transform-origin: right var(--dsx-rail-top, 0px)——即组件栏自身的右上角(距窗口顶约 44px,不在视口顶),在卡片区右缘之外。参数都在 设置 → 组件 → 外观与布局 → 打开与收起动画:
- 展开方式(三选一,默认「左右平移」):左右平移——每张卡片只沿水平方向依次滑入(translateX 单独一条通道,不缩放、不改变纵向位置),因此文字不会被重新栅格化,也没有共享锚点:卡片起点的高度与它在窗口里的位置无关,是三者里最省、也最不容易晃眼的一条。逐个落入——组件栏本身不动也不缩放,而每张卡片走的正是「整体缩放」时整块走的那条路径——同一个锚点、同一个起始缩放、同一段位移、同样两条曲线;不同的只有各自时机,而且整个折叠的总时长就是整块那条时长:一张卡片的行程与逐拍步进是从它里面切出来的(60% / 40%),所以 14 格落在 9.23ms 的拍子上,最后一张恰好在整块本该到位的时刻到位——级联从组件区左下角那一格开始、右上角那一格收尾,收起按同一条时间线倒放(所以快速反向时能从当前进度续播)。每一格都有自己的延迟(不封顶)。于是卡片是从组件栏右缘之外一张接一张飞进来(组件栏会裁剪自己的子树,卡片轮到之前不绘制),而不是整块一起到位。全程只有 transform,没有任何透明度变化;底部那颗设置齿轮是同一张网格里的一格,所以跟着一起联动。「整体缩放」是另一种取舍:整块滑入 + 缩放,所有卡片同时到位。
- 打开起始缩放(50–100%,默认 88%):展开起始的缩放——「整体缩放」下作用于整块,「逐个落入」下作用于每张卡片。100% 表示纯平移。
- 位移曲线(默认根号):位移的进度曲线——「整体缩放」下是整栏的水平滑入,「逐个落入」下是每张卡片各自的同一条滑入。
- 回弹幅度(0–20%,默认 4%):位移收尾时的过冲幅度——卡片(「整体缩放」下是整栏)会越过座位 回弹幅度 × 行程(默认 ≈ 18px,20% 时是 91px)再弹回来。这个数字就是过冲本身,不是控制点:代码会反解位移曲线的纵坐标,让曲线峰值恰好超过 1 这么多——正因如此两种展开方式仍是同一张映射:整块那条 CSS 过渡与逐卡级联算的是同一条 cubic-bezier。它只作用于位移是有意为之:所有曲线的纵坐标都被夹在 [0,1] 内,因为缩放过 100% 会让整栏长出它所在的列;所以缩放是单调的,回弹交给位移。默认值取自物理而非目测——同一族弹簧(Android DAMPING_RATIO_LOW_BOUNCY = 0.75、motion.dev 默认 bounce = 0.25 ≈ ζ 0.75)一次干净过冲约是行程的 3–5%,ζ = 0.70 时峰值落在时长约 57% 处,与本实现的 ~57% 一致。收起是同一条时间线倒放,所以这段回弹会变成离场前的一次短促蓄力,而不是留在结尾。
- 缩放曲线(默认根号):缩放的进度曲线(「逐个落入」下同样作用于每张卡片,由级联延迟错开)。
每条曲线就是那条 cubic-bezier() 本身:一条线段、两个可拖圆点(也可以用方向键微调),另给 5 个预设(线性 / 标准 / 缓出 / 根号 / 平滑)。预设只是这四个数字的命名取值,所以「自定义」是推导出来的(没有任何预设相等时显示),不存在第二份状态可能与图不一致。曲线下方的小圆点会实时重放这条曲线:横向匀速(时间)、纵向按曲线(进度)。
只显示完整组件(默认开)解决的是另一件事:整行挡位滚动保证了顶部不被截断,但视口高度不是行高的整数倍,所以最底下那张卡本来会被切掉一半。开启后这张卡既不绘制也不参与命中测试(它的位置是空白带,悬停不该唤醒波峰)。做法是给那一个卡片槽加一个类隐藏它,而不是把可视区域整体裁掉——可视区底边只在最后一张完整卡片下方 间距(默认 24px)处,而卡片的投影要伸出卡片本体约 28px,一刀裁下去会切掉你看得见的那张卡的阴影;隐藏卡片本体则让这条带子保持完整只是不画东西,所以每张可见卡片的阴影都是完整的。跨过边缘那一刻是淡出(opacity 0.18s,visibility 的翻转延后到淡出结束),滚回来时立即显示并淡入——visibility 不能插值,所以离散的那一半靠 transition-delay 延后,而延时只写在「被裁掉」这条规则上(transition 读的是变化之后的样式),于是裁掉=淡出、恢复=立刻出现再淡入。
这条规则判定的是组件栏真实的滚动可视盒:判定与挡位终点都读「栏自身 padding-top 的实测值」,而不是写死的 4px(2026-10-14)。原因是右侧列现在有同居者——置顶摘要卡片会把组件往下推 !important 的 padding-top(实测 79–131px),而栏的 clientHeight 一点都不变:旧实现按 4px 算,可视盒的底边就比真实底边低了整整一个「推动量」,于是被底边切掉一角的卡片被判为完整、连同探出屏幕的那半截一起画了出来(车主截图里最底下那排)。同一处修正也进了滚动终点公式(lastRow 用同一个实测 inset),所以被判截断的那张卡仍然滚得到——两半必须一起改,只改判定会让它永远消失。推动量在原地动画(约 260ms 内逐帧写样式)期间也跟得上:栏上一个 style 属性的 MutationObserver 以 rAF 合并重测,值没变就 Object.is 直接跳过。
底部那颗设置齿轮同样遵守这条规则(被切一半就不画),它同时是一个悬停才出现的入口:平时不绘制,指针进入组件栏(或放大层)时淡入。它的格子仍然保留在布局里——否则每次悬停都会改变栏高与滚动终点,卡片会跟着跳一下——被门控的只有绘制。它点开的是产品自己的设置窗口,并直接停在 设置 → 组件 这一页(2026-10-14):那是全应用唯一的组件配置入口,插件不再自造第二套界面。实现上它替用户点两下——侧栏底部的设置触发行,然后是我们的 组件 导航行(沿用 settings-nav-glyph 自己的 _settingsArea / _navList / data-dsx-nav 约定定位),窗口还没开时开窗后再等导航行出现。插件原有的那张自造面板(已装组件 / 组件市场 / 外观与布局三个 tab、可拖宽)保留在源码里仅作参考,没有任何挂载路径——没有构建开关或 localStorage 开关能把它重新接回。
滚动终点(2026-10-01):滚轮停在一个「最下面的组件刚好完整显示」的挡位上,而不是「最后一行顶到栏顶」。因为整行挡位保证顶部完整、整卡规则保证底部完整之后,再往下滚只会把最后一行往上拉、在下面留出一整条空白;现在栏底与最后一个组件之间最多只有一个卡片间距,浏览器的滚动范围也同步收到这个终点,触控板惯性冲不过去。
放大波峰是连续的:指针扫过时下面几行会被推开,被推出视口的那张卡仍会被视口裁掉——跟随裁剪意味着动画中卡片会不断闪现消失,所以静态判定(静息座位)是刻意的,波峰本身不受影响。放大层的左侧余量在收回过程中同样保留(overhang 现在跟随整个 morph,而不是只在「已激活」时计算):否则指针离开的第一帧余量归零,而卡片还有约 25% 的放大没退回去,最左那张会被 overflow: hidden 切掉一条——就是那个「高亮消失时左侧被截断」的现象。
波峰放大(动图)
指针沿组件栏滑过时,卡片按欧氏距离连续放大、下面几行被推开,离开时原路退回——不是两张图交叉淡入。
首屏那张静帧只是装好之后的静息状态;动效在这里:
480×784 · 13 fps · 88 色 GIF(3.8 MB);原速 mp4 见 rail-demo.mp4。
内置部件
| 部件 | 说明 |
| 轮次·步数 | 会话轮次与步骤计数 |
| LLM 时长 / 工具调用 | 推理与调用的累计耗时 |
| 首 token 平均 | 平均 TTFT |
| 速率 | 解码吞吐(tok/s) |
| 缓存命中 | 输入缓存命中比例 |
| Tokens | 输入 / 输出 token 计数 |
| 会话概览(2×4) | 把上面那排统计拆出来的会话数字并成一张 2×4:默认 轮次 / LLM / 工具 / 速率,最多 10 个,在组件配置里勾选与排序(metrics 字段:名称在左 · 开关在右 · 直接拖整行排序,没有把手;插入指示用的是产品自己那套蓝色箭头横线)。每行最多 5 个,超过折成两行(6→3+3、8→4+4、10→5+5)。可选 12 个数字(轮次、步数、LLM、工具、TTFT、速率、缓存、输入、输出、上下文、进行、待办)走的是与单卡同一套格式化函数,同一个数字不会因为看哪张卡而变义;一行挤到 5 个时带不下的单位会自动退回纯数字。两个数字之间的间距就是卡片自身的内边距,与「系统监控」的环图同一套规则。2×2 单卡的大数字是 20px,看板数字是 13px——看板用来扫一排,单卡用来看一个,两者并存 |
| 上下文水位 | 系统 / 工具 / 消息三段占比条 + 明细;支持 2×2 与 2×4 |
| 系统监控 | 本机硬件族:CPU/GPU 利用率数字、内存、显存、GPU 温度——2×2 卡片 + 2×4 环形看板,另有 GPU 利用率折线 |
| 一键压缩 | 上下文占用百分比 + 角落圆钮(双击执行压缩) |
| 任务 | 进行中 / 已完成 / 待办计数 |
| 用量热度图 | GitHub 式日历热力图,自记账每日用量;2×2 = 近 3 个月日历,2×4 = 近半年全部用量点 |
| 用量柱状图 | 最近 7 天的垂直柱状图;柱区高度与日历网格一致 |
| 对话轨迹 | 官方「轨迹」三色泳道的卡片版:输入 / 模型 / 工具 每触发一次画一根色条,随模型调用工具在最近 30 拍窗口里向右滚动。横向几何与官方时间轴一致:圆角 1px、间隔 min(条宽×8%, 1px)、不足 2px 按 2px 画,三条泳道始终都在(该泳道这一窗口内没有拍就是空轨道);纵向按卡片:三条泳道彼此贴紧、整体撑满灰色子标题到卡片内边距之间的全部高度(官方在 1300px 宽条上是 8px 条高 / 14px 间距,照搬到 160px 卡片会空掉三分之二)。泳道宽度可在组件配置里切换,对应官方工具栏的「时长」开关:按时长(默认,条宽 = 该拍耗时占窗口总时长的比例,空档被压缩掉)或 等宽(官方默认投影,一拍一格、等宽,槽位在 30 拍时冻结、不再随窗口滚动重算) |
| 今日寄语 | 随机鼓励语录;文字/对齐/换行可自定义 |
| OpenCode Go 用量 | 用量对比(三窗口柱状图)/ 用量环图(三窗口环形图)/ 滚动用量 / 每周用量 / 每月用量 |
| Command Code 账户用量 | 账户 / 用量(4.9M tokens 大字 + 底部 请求 · 成功率 · 消费 三栏)/ 额度(69.16 credits 大字 + 官网那套 5 小时 · 周 · 月 三条分段横条)/ 窗口 / 套餐(等级徽标 + 账期行),以及 5h 窗口 / 周窗口 / 月窗口三张单窗口数字卡 |
| 额度管理 | 2×2 月窗口版式:月末用量预测 + 今日用量 / 今日推荐 |
| 峰谷定价 | 仅 2×2:当前是否处于 DeepSeek 高峰计费时段(周末与中国法定节假日全天低谷) |
组件预览即卡面
组件市场与「组件配置」的预览把卡片钉在一个 unit 正方形里(pinBox),所见即轨道上落座的卡面:内容过高的卡片由卡片自身的 overflow: hidden 裁掉,而不是把预览台撑成一个非正方形的圆角矩形(额度卡改造前实测 200×250)。没有多余的角色词:数字自带单位(4.9M tokens、69.16 credits)或自带名字(等级徽标)的卡片,灰字行留给多池时的当前视图。
套餐卡(等级徽标 + 账期)
大数字是官方套餐表的等级徽标(individual-goat → GOAT、individual-pro-v1 → PRO …,与 provider 一样按最长前缀解析),不再打印原始 plan id(旧的 individual-goa… 直接顶出卡片边缘);图例行是账期,徽标落座卡片底部左下角。组件市场的预览点击会走完整条等级阶梯(GOAT → PRO → MAX → ULTRA → PROVIDER → GO → TEAMS PRO),没有那个套餐也能先看效果。
Command Code 账户池(点卡片切换账户)
Command Code provider 的「账户轮换」卡片可以把多个订阅挂在一个安装下(COMMANDCODE_API_KEY、COMMANDCODE_API_KEY_2 … _4)。host 侧的同源路由 /api/commandcode-usage 把每一把已配置的 Key 都读一遍(各自四个官方端点),连同每个 Key 自己 /alpha/whoami 读到的账户名一起下发:
- 点任意一张 Command Code 卡片即可循环:AllUser → Physicolor → Sparxie → AllUser。选择按卡片实例持久化在 cardConfigs.ccView,刷新不丢;它与 OpenCode 池的 poolView 是两个字段,两族互不干扰;
- AllUser 是整池合计,逐字段相加:窗口百分比 = Σused / Σcap(不能取百分比均值——一个用尽的池 + 一个没动的池,那是「用掉一半额度」,不是「每个池各用一半」);月窗口按各成员自身的月窗口相加,每个成员用自己套餐的额度计量,所以两个 GOAT 池就是 2 × $70;账户卡在合计视图里显示通用标签 AllUser,底下一行列出各账户名。AllUser 是刻意的中英同形表达;
- 图例行带出当前视图(账户 · AllUser、窗口 · Physicolor),与卡片角色词并列;悬停提示给出完整循环链;
- 只配置一把 Key → 完全没有切换器:卡片与改动前逐像素一致(图例行只剩角色词,账户名回到正文)。两个池解析出同一个账户名时,用 Key 的尾 4 位区分。
额度管理(Coding Plan 分组,2×2)
蓝色标题在顶,大数字(月末用量预测百分比)在标题下方一行,灰色子标题(账期 10-10)在大数字右侧、底边与大数字对齐,底部两个数字:今日用量(实测 token)与今日推荐(把剩余额度均摊到剩余天数得到的每日上限)。这套「大数字在标题下、灰字在右侧」的版式与「上下文已用」卡一致;标题与大数字之间是标准 4px 间距(150px 卡)。头部不再显示池视图名:点击卡片照旧在 AllUser → 账户… 之间切换(数字跟着变),只是不再有那一行标签。
- 口径(只算这个套餐自己):今日用量与今日推荐都按 Command Code 的 provider 路由(commandcode)折出来的每日日志计算(/api/widgets-usage-daily?provider=commandcode),与 credits、账期同源。同一台机器上并行跑着的其它 provider(例如 OpenCode Go)当天的 token 不计入——否则会把别人的用量算进这份套餐的额度(实测 2026-09-20:全机器 783M 里 474M 属该路由)。带口径的地图拿不到时两个数字显示 —,不会退回混合口径。
- 预测口径:已用% + 近期速率 × 剩余天数,近期速率取最近 3 个日当量(前 2 个完整天 + 今天按已过比例折算)。因此预测与今日推荐数学同调:预测 > 100% ⇔ 近期速率 > 今日推荐速率——卡片绝不会一边说「今天没超推荐」一边说「整月超 100%」。
- 超过 100%:数字本身变红并做 1.6s 呼吸闪烁,而不是卡片四周泛起红色光晕。
- 额度 → token 换算:余额(credits)按本账期已实现的「本地 token ÷ 消耗 credits」汇率折算成 token,两个数字同口径;不混入 provider 自己的 token 计数(它对同一账期的计量约高 1.7 倍)。新加的池成员本账期一分未花时没有自己的汇率,此时预算借用整池已实现的汇率(剩余额度 ÷ 剩余天数仍然成立),而速率侧保持它自己的 0——本地日志是整台机器的,不会算到没出过力的账号头上。
- 永远不打印「数据不足」,缺什么就填什么:还没拿到载荷 → 大数字 -%,能读到账期就照常显示账期,两个数字走 —;账期太年轻推不出预测(今天刚开的池成员)→ 显示它真实的已用百分比(0.0%)、真实预算与 0 用量;从未用过的成员 → 除了预算全是 0。只有「账期已结束」这类过期载荷才什么都不显示,因为它不再描述任何可花的东西。
GitHub
市场里新增一个 GitHub 分组,五张卡由一条 host 同源路由撑起——GET /api/github?user=<登录名>&repos=<owner/name,…>,浏览器不直连 api.github.com。凭据按三级阶梯解析,这就是它在任何人机器上零配置可用的原因:credentials.resolve('GITHUB_TOKEN' | 'GH_TOKEN') → 本机 gh auth token(跑过一次 gh auth login 就有)→ 匿名。两片数据各自在 host 侧缓存(日历 30 分钟、仓库脉搏 15 分钟),因为匿名 GitHub 是每个出口 IP 60 次/小时,与机器上其他 GitHub 工具共享。
两个配置项都可以留空:user 留空 = 「本机登录的那个账号」(从 token 自己的 GET /user 解析),repos 留空 = 该 token 能看到的最远推送的四个仓库。
| 组件 | 说明 |
| 提交热度图(2×2 / 2×4) | GitHub 同款 5 级绿色阶,颜色从 --dsw-alias-state-success-primary 派生(不是硬编码 hex),跟随亮/暗主题;复用 Token 日历同一个渲染器(新增 heatmapPalette: 'github')。2×2 = 近 3 个月(13 个周列),2×4 = 近一年(53 个),即 GitHub 个人页表头那个窗口。有 token 时走官方 GraphQL contributionsCollection(一次调用拿到逐日精确条数),没有则抓公开贡献页解析——两条路逐日结果已实测一致(docs/probe-github.mjs,两种阶梯各跑一遍)。 |
| Stars(2×2) | 大数字是 stars,标题下是仓库名,副行是 forks |
| 问题(2×2) | 未关闭 Issue,不含 PR(仓库字段的 open_issues_count 两者都算),副行是其中没有任何人回复的数量——这一项需要 token,没有时如实说明,而不是打一个没人测过的 0 |
| 提交(2×2) | 距最近一次 push 多久(12s / 5m / 3h / 6d),副行是最新 Release tag |
| 仓库脉搏(2×4) | Stars / 未关闭 Issue / 未回复 / 最近提交 并排,标题行给出仓库全名与最新 Release |
监控多个仓库时,每张仓库卡都带同一个点击循环(ghRepo),点任意一张就切换整个家族——与用量 / Command Code 的池切换同一套行为。
组件市场
- 浏览全部组件(系统 + 外部)、搜索、尺寸切换预览、按 组件@尺寸 安装;
- 已安装列表支持拖动排序、配置编辑,以及 2×2 ↔ 2×4 一键切换(自动去重——同一组件同一尺寸只保留一个实例);
- 已装组件 tab 支持卡片级自定义(今日寄语、热度图窗口对齐等)。
OpenCode Go 用量
滚动 / 每周 / 每月三个用量窗口 + 百分比 + 重置时间。Host 半注册同源路由代理 opencode.ai;浏览器不发任何跨域请求,密钥走 DSH credentials。两种呈现:用量对比(三窗口柱状图)与用量环图(三窗口环形图——环中心百分比、悬停显示精确值、按同一紧急度配色)。
DeepSeek 余额
一张 2×2 卡,通过 host 同源路由 /api/deepseek-balance 读 DeepSeek 官方开放平台账户——密钥来自 credentials.resolve('DEEPSEEK_API_KEY')(环境变量 → $DSH_HOME/.credentials.yaml → .env),浏览器永远看不到它,也没有任何输入框要你填。
| 卡面 | 是什么 |
| ¥110 | 官方总余额(GET https://api.deepseek.com/user/balance) |
| 刚刚更新 · 赠送 ¥10 | 这次采样的年龄,后面是赠送余额(只有账户真的有赠送时才印) |
| 充值 / 今日消耗 / 对话消耗 | 充值余额、今天的消耗,以及本次对话花掉的部分 |
「今日消耗」是量出来的,不是查出来的。 官方接口只给余额,没有用量也没有历史,所以 host 的做法是采样余额(当日台账落在 profiles/web/dsh-widgets-balance.json),把观测到的下降累加起来:充值或赠送不算负消耗,跨天(或换币种)重新起算。因此它覆盖的是 dsh 真正运行的那些时段——这件事写在悬停提示里,而不是让这个数字假装成一张账单。对话消耗复用 session-cost 的同一套纯函数定价(命中的规则带峰谷、而会话记录没有逐请求时间戳时印 ≈);没有价格表时印本会话的 token 并带单位,而不是编一个费率。
颜色在这里表示等级,这也是它所属家族里唯一用色的卡:低于你自己设的阈值(每卡设置「余额预警阈值」,默认 10)大数字转琥珀色,低于阈值一半转红色;is_available: false(DeepSeek 已停止为该账户提供服务)为红色,原因在悬停里。「没有数字」的每一种情形各有各的说法——没配密钥、密钥被拒、host 没重启、超时——而不是笼统一句「暂不可用」。
峰谷定价(市场组件)
仅 2×2 的峰谷定价卡片,显示当前是否处于 DeepSeek 高峰计费时段。高峰时段(北京时间 UTC+8)为周一至周五 09:00–12:00 与 14:00–18:00——按官方定价页的说明,其余时间一律为低峰,中国法定节假日全天(不是「工作日之外才便宜」)以及调休上班的周末同样按低谷计费。后半句不是细节:只看工作日时钟,2026 年会把 19 天当成高峰报价(元旦 1/1–1/2、春节 2/16–2/20 与 2/23、清明 4/6、劳动节 5/1 与 5/4–5/5、端午 6/19、中秋 9/25、国庆 10/1–10/2 与 10/5–10/7)。低峰显示 CHEAP;处于高峰窗口时显示 EXPENSIVE,数字本身变红并做 1.6s 呼吸闪烁(文字级告警,卡片边框保持干净),同时标题下方对应的时段行亮起品牌蓝并微微放大。整天低谷的日子,标题下的计量行直接写明原因(中秋节 · 全天低谷 / 周末 · 全天低谷),而不是点亮一个根本不生效的时段。以上全部可按卡片配置:高峰时段、周末/节假日开关、时区(北京 UTC+8 或本机时区),以及一个额外低谷日列表(用于国务院尚未公布下一年放假安排的年份)——这种年份卡片会退回「工作日 + 周末」判断,并把这件事写进悬停提示,而不是猜。法定假期区间放在 src/widgets/peak-pricing/holidays.ts,整套规则由 docs/verify-peak-pricing.cjs 复核(34 条断言)。
工作原理
- 部件单元 + 构建期发现(ARCH-001):每个部件都是 src/widgets/<id>/ 下的独立单元——manifest.json(机器可读的那一半:id / order / group / builtin / defaultInstalled / sizes / 该部件的 locale,另可选 source 与 skeleton)+ index.ts(defineWidget 描述符:纯数据的 render()、名称/描述 thunk、configSchema、example)。注册表是生成的,从不手工维护:scripts/gen-registry.mjs 扫描各单元目录、拒绝未知 manifest 键与 id 三处不一致,并产出 src/client/generated.registry.ts(WIDGETS / WIDGET_RUNTIME / ALL_IDS / ALL_INSTANCES / STATS_WIDGET_IDS / DEFAULT_INSTALLED / 合并后的 WIDGET_LOCALES)。新增一个部件 = 新增一个单元目录;pnpm build 会重新生成,注册表过期时 pnpm check:registry 会直接报错。部件模板放在 src/widgets-template/——在扫描根之外,因此永远不会被发现或注册;
- 契约:src/client/lib/contract/——types.ts(单元 / 渲染层 / 外壳之间交换的全部形状,零 import:只想要类型的消费方不会把 i18n 或 React 拖进自己的模块图)+ helpers.ts(defineWidget、标签解析器、实例键、sizesOf)。其余与框架无关的工具并排放在 lib/ 下(format.ts、heatmap-accounting.ts、quota-math.ts、morph-spring.ts);某个数据源的载荷解析与该源的卡片渲染在 src/client/families/<族>/{data,renders}.ts,族与族之间互不引用;
- 按部件 i18n:部件文案放在各单元的 manifest.json(同族共用文案放 src/widgets/_shared/locales.json 一次);外壳字典(src/client/i18n.ts)只管外壳 UI。生成的注册表把一切合并,外壳在 apply() 时向官方 locale 服务注册;
- 组件栏由三个模块构成:rail/geometry.ts(空间与预算解算,纯计算)、rail/measure.ts(锚点探针、宽度跟踪、ResizeObserver 扇出与让位节拍——直接写 DOM,因此不会比 React 的提交晚一帧)、rail/rail-view.tsx(createRailView(deps):网格、放大波、加号面板与滑动抽屉)。client/index.ts 只做装配——bridge、四个槽位注册、页头胶囊与设置页(308 行);
- 放大波:曲线在 lib/morph-spring.ts(WAVE_SPRING),布局与比例场在 rail/wave/wave-geometry.ts,交互与逐帧写样式在 rail/wave/RailWave.tsx;
- 数据收集器:挂载在 conversation.composer.dock slot,该 slot 仅在存在活跃会话时渲染——天然的「会话存活」信号;
- Host 半:src/host/——一个上游渠道一个模块(opencode / commandcode / usage-daily / github / deepseek / sysinfo)外加状态文件,共用 host/http.ts(memo 与请求体读取)、host/exec.ts(唯一的子进程缝)与 host/context.ts(ctx 契约)。src/index.ts 是 31 行,只组装 HOST_ROUTES。路由:/api/opencode-usage、/api/opencode-usage-multi、/api/commandcode-usage、/api/widgets-usage-daily、/api/github、/api/deepseek-balance、/api/widgets-state(组件栏配置的权威副本,持久化到 profiles/web/dsh-widgets-state.json——浏览器换 origin、无痕模式、清除站点数据都丢不了)与 /api/sysinfo;
- 可逆清理:所有注册都由 fiber 的 effect 生命周期管理;卸载即完全恢复;
- Slot 接入:conversation.input.overlay(组件栏抽屉、放大浮层与设置抽屉——刻意放进对话子树,它会画在官方右栏面板之下,面板因此能吞掉组件栏)、conversation.session.header.utilities(「组件」胶囊,注册在 order: 5,避免与 dsh-better-sidebar 的底部面板开关撞号而换位)、conversation.composer.dock(数据收集器)、settings.section(设置页);
- 空间契约:组件栏从不索取固定宽度。它的预算是「官方对话列宽 − 官方正文 measure − 74px 盒内缩」,两个数都读产品自己发布的变量并带几何回退;在预算内它是一个流式网格(repeat(auto-fill, minmax(基准边长, 1fr)) 的语义:列数按基准边长自动填充、上限为用户设置;卡片边长 = 该列数下的等分宽度,按 10px 档位量化,夹在自动下限与「5 行可见」上限之间),因此正文始终保住产品自己的 measure。空间连单列都放不下时才让位。实测见 CHANGELOG.md。
安装
通过 npm(插件市场)
dsh plugin --profile web add dsh-widgets
本地开发(link 方式)
dsh plugin --profile web add link:D:/dsh-home/plugins/dsh-widgets
安装后硬刷新浏览器(Ctrl+Shift+R),在会话页头部点击「组件」图标即可展开组件栏。OpenCode Go 部件需先在 Models 设置中配置 OPENCODE_GO_API_KEY。
开发
pnpm install
pnpm run build # gen-registry + tsdown 打包 + 发布用 .d.ts
pnpm run check # 注册表最新性守卫 + tsc --noEmit(0 错)
pnpm run check:types # 用一个真实消费方验证两个入口的类型能解析
pnpm check:registry # 仅注册表发现守卫
node scripts/validate-widget-unit.mjs [dir] # 部件单元契约校验器(Worker 自检 / 评审)
本仓库类型检查是干净的,所以 pnpm check 是硬门;node scripts/gates.mjs 跑全部离线闸门(G1–G14),不需要浏览器、不需要网络、不需要起服务,每条证明什么写在闸门清单里。哪里实现什么,看 docs/architecture/CODE_MAP.md;每一步用什么证明「没改行为」,看 docs/architecture/ARCHITECTURE_REFACTOR_REPORT.md。
- peerDependencies:@deepseek-ai/cordis、@deepseek-ai/dsh-client-ui-slots(由 DSH web profile 提供;@deepseek-ai/dsh-client-runtime 已在 DSH 0.1.5 退役);
- cordis.patch.yml 插入一行 widgets;host 半与浏览器半分别由 loader 与 client-modules 加载。
兼容性
- DeepSeek Harness 0.1.0-rc.6 起的 0.1.x,以及 0.2.0-rc.1 起的 0.2.x——浏览器与桌面端都可用;
- 通过 conversation.input.overlay / conversation.session.header.utilities / conversation.composer.dock / settings.section 接入;
- 与 dsh-better-sidebar 的右栏显式协调:组件栏读取官方右栏列(旧版 better-sidebar 的 --dsh-sidebar-width 保留为回退),页头胶囊注册在 order: 5,因此 bundle 重载不会让两个开关互换位置;卸载后无残留。
发布
每个版本的逐条记录——连同每条改动背后的实测数据——都在 CHANGELOG.md(中文);每个版本同时以 GitHub Release 发布,锚定到实际发布它的那个 commit。原始证据(CDP 探针、截图、JSON 回执、逐事件的修复记录)在 docs/ 下。本 README 只保留当前版本的速览。
本次更新 — v1.8.5
又多了六张卡,而且每张卡背后的数据现在由自己声明——注册表从 55 个单元长到 61:
- 时间族——时钟(指针或数字、六个时区,秒针真的在跳)、日历(有 DSH 定时任务的日子打点;2×2 月历,2×4 多一列临近日程)、任务计时器(本回合已跑多久、此刻在等什么)、秒表(开始 / 暂停 / 复位)、倒计时(时长胶囊,或最近的定时任务);
- DeepSeek 余额——官方账户余额,旁边是账本推导出的当日花费;它读自己那条 host 路由(/api/deepseek-balance),折叠逻辑由一个新增的离线闸门守着;
- 数据源声明式契约——单元自己说清楚读什么(reads / source / 骨架屏),一个通用轮询器替所有人应答;声明与单元漂移时构建直接失败;
- 四个缺陷消失——同住者把组件栏压下去之后,悬浮波峰不再在指针下面一整行处激活(45 项:25 FAIL → 45/45 PASS);shell 换节点后会话头部锚点不再冻结;整卡判定与滚轮终点开始读同一个实测 inset——而且 paneH 终于被量到了(此前从未);nvidia-smi 的 MiB 不再被当成字节透传;
- 「设置」齿轮改为打开 设置 → 组件——应用自己那一页,而不是我们自造的第二套面板(那套面板保留在源码里仅作参考,已无挂载路径);
- 两份有牙齿的卡片标准——docs/design/CARD-BODY-POSTURE.md 与 CARD-TITLE-STANDARD.md,都是「过了当时所有闸门的事故」催生的,现在都由 scripts/probe-geometry.cjs 机器断言;
- CI 与可在 CI 里跑的离线闸门——node scripts/gates.mjs(G1–G14),以及 .github/workflows/ci.yml:每次 push 与 PR 都跑 pnpm run build + pnpm test。
以上每一条连同背后的实测数据,都写在 CHANGELOG 里。
路线图
组件系统现在是为规模而建的:每个部件都是 src/widgets/ 下独立、契约驱动的单元,并在构建期被发现——新增一个部件就是新增一个单元目录,无需改动任何共享文件(指南:src/widgets-template/README.md)。
- 热度图 token 口径的后续工作:卡片的总量已经与 dsh-usage-center 一致(host 路由复用它的 getActivity())。要在未安装 usage-center 时也做到精确,就得在本 host 里折叠同一批会话日志——今天刻意不重复实现,以保持口径只在一处维护;
- Command Code 月窗口,待上游字段:月份由本地推出 used = 套餐额度 − 剩余月额度(额度取自公开套餐表),因为 billing/credits 不返回月窗口对象。若上游之后在 windowLimits 里补上 monthly(含 used / cap / resetAt),monthlyWindow() 应直接读它,推导只作为回退;
- Command Code 池靠命名约定发现:host 读 COMMANDCODE_API_KEY 以及 provider「账户轮换」卡片写下的 _2 … _4 备用 Key。若某把 Key 存在无关的 ref 名下,需要把该 ref 加进 COMMANDCODE_POOL_ENVS(credentials 服务只暴露 resolve(ref),没有「列出所有 ref」的口子,无法自动枚举);
- Agent 生产部件:机器可读契约(manifest.json + defineWidget 描述符 + 模板 + 共享 API)正是一个 worker agent 端到端造出一个部件所需要的一切;v1.3.0 的并行创建测试证明两个 agent 可以同时添加部件、零文件冲突;
- 更多硬件指标:通过可选的 LibreHardwareMonitor 桥拿 CPU 温度(外部依赖、需显式开启——刻意不打包)、NVIDIA 之外的 AMD/Intel GPU 支持、按网卡的流量;
- 热度图范围/周期控制:让 2×4 热度图与柱状图能选当前半年 / 7 天默认值之外的自定义范围(周/月等);
- 更多用量提供方:Z.ai 等,复用 host 同源代理 + credentials 模式(DeepSeek 官方余额卡已在 v1.8.5 上线);
- 峰谷定价的节假日表,每年一次:法定假期区间放在 src/widgets/peak-pricing/holidays.ts,逐条抄自国务院办公厅的年度通知(2026 年 = 国办发明电〔2025〕7号)。下一年的安排通常在前一年 11 月公布——届时补上区间并扩展 HOLIDAY_YEARS 即可;在此之前卡片退回「工作日 + 周末」判断、在悬停提示里说明这一点,并可用卡片级的额外低谷日字段补齐。同一张卡上仍待做的:工作日集合自定义(时段与时区已经可改);
- 实用工具部件:一键压缩(需要 DSH 官方 compaction 能力)等;
- 外部集成:飞书 / 微信推送与交互,密钥严格走 DSH credentials;
- 部件市场:开放第三方部件注册机制,让社区部件像插件一样入驻——单元 + 发现架构(v1.3.0)就是载体;未来的 widgets-market bundle 可以同样地把单元放进 src/widgets/;
- 更多语言:字典层每个 key 都已有 zh/en——加 ja/ko 等纯属字典扩展;
- 跨设备同步(可选):今天每台 DSH 服务各存一份 dsh-widgets-state.json——云/账号同步层可以让多台机器共享一份配置,但「本地优先、设备独立」是刻意保留的默认行为。
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Physicolor/dsh-widgets
description: "右侧实时组件栏:会话、机器、额度与成本,一眼看完。"
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,344已汉化DSH
71.5
终端编码代理esengine/DeepSeek-Reasonix
· 指南 · ACP · 扩展开发 · 规格 · 官方网站 · Discord · QQ
★ 36,045已汉化DSH
71.2
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,202已汉化DSH
71.1
这是你的仓库?认领条目