安装
在 DeepSeek Harness 里通过 dsh-market 安装
dsh plugin --profile web add dshmarket
或使用命令行
dsh plugin --profile web add github:wjingshan/dsh-cost-gauge
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。
截图
README
中文 | English
DeepSeek Harness(dsh)的花费指示器:Web 界面左侧靠上的方形浮动窗,实时显示会话花费与余额;半圆表盘指针随北京时间走动,弧色按当日费率带显示(绿=标准 / 黄=高峰,周末与法定节假日全天绿);余额低于阈值时顶部红灯报警;支持拖拽缩放、最小化(状态灯 + 倒计时饼图 + 模型徽标),以及贴边吸附(拖到大窗口左右边缘会自动吸附并变形成竖条)。
🔀 相关项目:v1.4 起新增的「极简时钟 / 多皮肤」改版线已独立为 dsh-cost-gauge-plus;本仓库保持 **v1.0 经典形态(方形指针表)**继续开发,两者互不干扰。
截图
| 展开态 | 最小化态 | 节假日态 |
|---|---|---|
左:展开态(工作日高峰:表盘绿/黄双色、黄色弧段=高峰时段;底部三行为本回合花费 / 会话花费 / 余额);中:最小化态(费率灯 + 所剩时间饼图 + 会话费/余额 + 模型缩写徽标);右:节假日态(中秋 9-25,整弧全绿、状态写明「标准(中秋节)」)。
本次更新(v1.6.1 – v1.6.8)
最近这批更新分六拨:v1.6.8 新增本回合花费、并让紧凑态可按需切换;v1.6.7 修好了在桌面端里点不动、拖不动(Electron 把窗口顶部的鼠标事件当成拖窗口吞掉了);v1.6.6 修好了花费记录面板里的柱状图(从 v1.6.0 起被玻璃化背景层盖住);v1.6.5 是最小化胶囊瘦身;v1.6.4 是边缘吸附(贴边竖条);v1.6.1 – v1.6.3 是一轮面向 dsh 0.1.7 的适配 —— 兼容性、节假日显示、花费显示修正。
| 版本 | 主题 | 一句话 |
|---|---|---|
| v1.6.8 | 🧮 本回合花费 | 展开态新增「本回合花费」(只算当前回合,不足 1 分显示 <0.01);最小化胶囊与贴边竖条可在设置里切换显示本回合还是会话花费 |
| v1.6.7 | 🖱 桌面端可交互 | DSH 桌面端用 -webkit-app-region:drag 把窗口顶部铺成拖动热区,浮窗停在那儿时鼠标输入被系统拿去拖窗口——点不亮、拖不动 |
| v1.6.6 | 📊 柱状图回归 | 玻璃化背景层与图表柱子同名(都叫 .dsg-bar),柱子被撑成 30 块铺满图表区的绝对定位层,最后一根的空底色把整张图盖成空的 |
| v1.6.5 | 📏 最小化瘦身 | 最小化态尺寸收一半、宽度按内容自适应;徽标槽位类被 setModelBadge 冲掉,右侧这才空一块 |
| v1.6.4 | 🧲 边缘吸附 | 把浮窗拖到大窗口左右边缘会自动吸附并变形成贴边竖条;点一下或拖离边缘即恢复原样 |
| v1.6.3 | 💰 花费显示修正 | 0.1.7 把「当前选中会话」移出客户端 sessions 服务,会话花费与模型徽标恒为 —,改为多源取值 |
| v1.6.2 | 🗓 节假日显示 | 中国法定节假日(含调休补班)全天按空闲档计费:表盘全绿、切换时刻跳过节假日、文案写明原因 |
| v1.6.1 | 🧩 兼容性 | 0.1.7 删掉了三栏框架上的 data-dsh-frame —— 对话区几何量错、侧栏收起检测失灵,改为反查框架 |
v1.6.8 · 本回合花费 + 紧凑态可切换
新增:展开态多一行「本回合花费」;最小化胶囊与贴边竖条可以切换显示本回合花费还是会话花费。
- 本回合花费:只统计当前回合。宿主遇到
turn/start就把该值清零重算,之后本回合的每一笔累加上去。 - 与会话花费同一套口径:逐笔沿用「事件时刻的费率 + 当时的模型」计价(和会话花费走的是同一条
priceAt路径),所以两个数字不会各算各的。 - 不足 1 分显示
<0.01,免得一整列¥0.00。会话花费那一行仍按原来的¥X.XX显示。 - 紧凑态二选一:设置里新增开关「最小化与竖条显示本回合花费」,默认关 —— 即最小化胶囊与贴边竖条都显示会话花费(保持原行为)。两个紧凑态一起跟着走;数字悬停会显示它到底是哪一个。
- 归零互不影响:手动点「会话花费归零」只动会话花费,本回合花费不动。
- 替换与重试都算对:同一
turn/step的重复样本按官方投影口径「替换」而不是累加,llm/retry-started的重试撤销也会从本回合里一并扣回。
| 场景 | 显示 |
|---|---|
| 展开态 | 本回合花费 / 会话花费 / 余额,三行 |
| 最小化胶囊,开关关(默认) | 会话花费 |
| 最小化胶囊,开关开 | 本回合花费 |
| 贴边竖条 | 与最小化胶囊始终一致 |
| 本回合花费 < ¥0.01 | <0.01 |
宿主半边(回合记账)与客户端半边(新行 + 开关)分开改。宿主半边被 DSH 的模块缓存钉住,改了要重启 DSH;客户端半边刷新页面即可。
v1.6.7 · 桌面端里点不动、拖不动(Electron 把鼠标事件当成了拖窗口)
修复:在 DSH 桌面端(Electron,0.2)里,浮窗如果停在窗口最上方,单击展不开,一拖就是把整个应用窗口拖走。
DSH 桌面端在窗口顶部铺了一条窗口拖动热区:
[data-windows-titlebar] .frame:before{inset:0 0 auto;
height:var(--dsh-windows-titlebar-height);-webkit-app-region:drag}
Chromium 把这条带子当作窗口标题栏:落在里面的鼠标输入被系统拿去拖窗口,根本不会投递给页面。浮窗位置存在 dsh-cost-gauge:pos,拖到顶部会被钳到 y = 8,整颗缩小胶囊正好都压在这条带子里 —— 于是点击收不到 click(展不开),拖动被当成拖动窗口。
浏览器里的 dsh web 没有 app-region 这个概念,走的是普通 DOM 命中测试,所以这个毛病只在桌面端出现。
- 给
.dsg-root与.dsg-records各加一条-webkit-app-region:no-drag;app-region:no-drag;,把浮窗自己的矩形从热区里挖掉。 - 依据是上游自己的做法:DSH 那个
position:fixed的侧栏开关同样带着-webkit-app-region:no-drag——「fixed 元素压在热区上必须自己声明 no-drag」是既定约定。
纯 CSS、只动客户端半边,不需要重启 DSH;bundle 换了 rev,刷新一次页面即可生效。改动已在运行中的桌面端宿主上核对:旧 rev 返回 404,重组后的 rev 返回 200 且含上面两条声明。
v1.6.6 · 柱状图被背景层盖住(类名撞车)
修复:花费记录面板的柱状图从 v1.6.0 起就只剩「最大 ¥…」和图例,柱子全不见了。
根因是两个东西共用一个类名:图表柱子(v1.3.0 引入)叫 .dsg-bar,玻璃化改造(v1.6.0)给标题栏 / 底部行加的背景层也叫 .dsg-bar:
/* v1.6.0 的背景层(玻璃化时才显示) */
.dsg-bar{position:absolute;inset:0;z-index:1;display:none;pointer-events:none;background:rgba(20,22,28,1); …}
/* v1.3.0 的柱子:位置更靠后,但只声明了这几个属性 */
.dsg-bar{flex:1;min-width:0;height:100%;display:flex;flex-direction:column;justify-content:flex-end}
后一条虽然更晚,却只覆盖 display / height / flex,柱子于是把背景层的 position:absolute; inset:0; z-index:1; pointer-events:none 全继承下来 —— 30 根柱子各自铺满整个图表区,后画的盖住先画的;最后一天没有用量、没有色段,它那层不透明底色正好把前面所有柱子盖干净。pointer-events:none 一并继承,所以柱子连鼠标都点不到(document.elementFromPoint 命中的是容器而不是柱子)。
- 把背景层限定成直接子元素:
.dsg-root>.dsg-bar{…}、.dsg-root.dsg-frosting>.dsg-bar{display:block}—— 背景层本来就是.dsg-root的直接子元素(标记里<div class="dsg-bar">与.dsg-bg、.dsg-frost并列),图表的柱子是它的后代,从此互不干扰。 - 顺手在那条规则前加了注释说明这个撞名,免得以后又写出一条裸
.dsg-bar。
| 场景 | 改前 | 改后 |
|---|---|---|
| 面板里的柱状图 | 空的深色矩形(只剩「最大 ¥2.74」+ 图例) | 30 根堆叠柱 + 日期标签 + 图例 |
| 柱子(图表)的定位 | position:absolute、不透明底 |
position:static、透明底 |
| 玻璃化背景层 | display:block / absolute / z-index:1 / rgb(20,22,28) |
不变 |
纯 CSS 改动,只有两处选择器;在项目自带的实时预览页(
docs/preview-live.html?panel=1&range=month&view=band,同一份lib/client.js)和真 GUI 里都验证过。
v1.6.5 · 最小化胶囊瘦身
修复 + 调整:最小化状态右侧总是空一块,整体尺寸再收一半。
原来最小化是一个 210×58 的宽胶囊,模型名一短(比如 flash)右边就空出六七十像素。根因不在尺寸,在徽标 —— setModelBadge() 每次都整个重写 className:
el.className = 'dsg-badge' + (cls ? ' ' + cls : '')
于是槽位类 dsg-mini-badge 被冲掉,.dsg-mini-badge{margin-left:auto} 从未生效;又因为宽度写死 MINI_W = 210,空出来的地方就留在右边。(竖条徽标之所以没坏,只是因为那里额外补了一句 classList.add('dsg-narrow-badge')。)
- 徽标槽位改用参数传入:
setModelBadge(el, modelRaw, slot),三个调用点分别传undefined/'dsg-mini-badge'/'dsg-narrow-badge',顺手删掉那句补丁。 - 宽度改为按内容自适应:最小化态设
width:auto,并同时关掉container-type——.dsg-root带着container-type:inline-size,是 inline-size 容器、固有宽度忽略内容,只写width:auto会塌成 22px(实测)。 - 整体收一半:状态灯 40 → 20px,主数字 14 → 12px,余额 11 → 10px,内边距 8px/10px → 3px/7px。
- 徽标字号改定值
9px(最小化态已无容器,cqw会退化成视口单位)并限宽44px,长模型名靠 ellipsis 截断。
| 场景 | 改前 | 改后 |
|---|---|---|
长模型名 v4-flash-vision-exp |
210×58 | 135×34 |
短模型名 flash |
210×58(右侧空 ~62px) | 111×34(无空白) |
展开态与贴边竖条都没受影响(真 GUI 实测:展开仍 216×265、竖条仍 36×198)。本项同样是纯前端改动。
v1.6.4 · 边缘吸附(贴边竖条)
新功能:把浮窗拖到「大窗口」的边缘会自动吸附,左右边缘还会变形成竖条。
- 拖动时就有磁吸预览:指针进入边缘 24px 以内时浮窗会被吸过去,松手才真正落位 —— 不用猜「到底会不会吸上」。
- 左 / 右边缘 → 竖条:松手后自动变形成 36px 宽的竖条贴边停靠,自上而下显示状态灯、会话花费、余额与模型徽标;垂直位置随你拖到哪儿就停在哪儿。
- 上 / 下边缘 → 只贴边、不变形:顶边、底边只做吸附(横着变条没有意义)。
- 四角归左右:拖到角落时按左右边处理 —— 因为只有左右会变形成竖条。
- 退出很轻松:点一下竖条即可就地展开、回到拖之前的位置;或者把竖条往窗口中间拖,松手即解除停靠、就地展开。
- 状态持久化:停靠在哪条边、多高,以及原来在哪儿,都记在
localStorage(dsh-cost-gauge:dock/dsh-cost-gauge:pos),刷新页面后原样恢复。 - 可以关掉:设置面板新增「边缘吸附」开关(默认开);吸附距离也可用
localStorage的dsh-cost-gauge:snapDist覆盖。
这和 v1.4.0 的「竖版最小化」不是一回事:那个由窗口宽度自动触发(窗口窄了才收成小条),这个是按位置触发 —— 窗口再宽,拖到边上也照样吸附。 本项是纯前端特性,宿主半身(
lib/index.js/lib/ledger.js)一行未动。
v1.6.3 · 花费显示修正
修复:会话花费与模型徽标显示不出来(dsh 0.1.7 起,界面顶部只剩 —)。
DSH 0.1.7 把「当前选中哪个会话」从客户端的 sessions 服务里移了出去:dsh-api-session-controller 的列表快照现在只有 ids / byId / phase / projectionsBySession —— 没有 current 字段(该文件自己的注释就写着 "Host catalog and local reference allocator; view selection remains outside the Controller")。选中状态改由 dsh-client-ui-workspace 私有维护,并且没有 provide 成服务(0.1.7 客户端总共只提供 8 个服务:sessions / layout / resources / modules / documentPreviews / uiRenderer / sidebarRightTabs / sidebarRight),插件无从注入。
插件原先读 ctx.sessions.list.getSnapshot().current —— 在新版恒为 undefined,于是请求退化成不带 ?session=,宿主拿不到会话就只返回到费率与余额,界面顶部只能显示 —。
- 改为多源取值(新增
currentSessionId(),按可靠性依次尝试,任一成功即用):localStorage['dsh.sessions.current']—— DSH 自己持久化的选择(形如{ sessionId })。dsh-client-store的attachPersistence在每次状态变化时同步写回,所以它是实时信号,不是陈旧缓存。[data-sidebar-right-session]:not([hidden])—— 右栏当前会话元素,同样由 DSH 自己写入。- 旧版快照的
.current—— 兼容 0.1.6 及更早。
- 三处调用点统一:轮询取数、工作灯、记录面板/导出原本各自读
.current,现在都走currentSessionId()。 - 连带修好「工作灯」:
isSessionRunning()同样依赖.current,所以「会话执行中」的标题灯发光与最小化态闪烁此前一直是坏的(永远不亮)。
v1.6.2 · 节假日显示更新
节假日费率:中国法定节假日(含调休补班)按空闲档计费。此前表盘只按「星期几」判断,节假日一旦落在周一至周五,就会被错误地显示成标准/高峰。
- 新增
lib/holidays.js:内置 2026 年国务院办公厅节假日安排(元旦 / 春节 / 清明 / 劳动节 / 端午 / 中秋 / 国庆),并区分法定节假日与调休补班的周末(如 9-20 周日、10-10 周六照常上班)。 - 节假日表盘全天全绿:节假日当天(即使落在周一至周五)与周末一样整弧绿色,不再画出高峰黄段 —— 与官方「节假日按空闲时段计费」一致。
- 费率切换时刻顺延:倒计时不再简单地跳到「下一个 9:00 / 12:00 / 14:00 / 18:00」,而是跳过节假日与周末。例如中秋前一天 18:30 会一路顺延到 9-28(周一)09:00;国庆前一天 19:00 顺延到 10-08(周四)09:00。空闲段的「总时长」也按同一规则计算,倒计时饼图的比例仍然正确。
- 状态文案说清原因:工作日白天显示「标准」原本会与表盘上的黄段自相矛盾,现在直接写明 ——
标准(中秋节)/标准(调休上班)/标准(空闲)。 - 数据覆盖提示:设置面板版本行会显示
节假日数据:2026 年(国务院办公厅通知);若当前年份尚未收录,则退化为仅按星期判断并转为橙色:⚠ 节假日数据未收录 2027 年,费率仅按星期判断。
明年(2027)的放假安排公布后,只需在
lib/holidays.js里补一张年份表;coveredYears()会自动把新年份带进上面的提示文案。
v1.6.1 · 兼容性更新
适配 dsh 0.1.7:该版本把三栏主框架上的 data-dsh-frame 属性删掉了 —— 框架上只剩 data-sidebar-collapsed / data-rightbar-collapsed / data-panel-conversation 这类条件性属性(不成立时根本不渲染),主框架不再能被直接选中。
- 修复:对话区几何量错(实测确认)。
getChatAreaRect()原先靠[data-dsh-frame]拿三栏框架,再在它的直接子节点里挑出「中间对话列」。0.1.7 下该属性取不到,函数会静默退回到全局搜索scrollBody:实测量到 998×732,而真正的中间列是 1000×808(高度少了 76px),并且内部的chatPanelEl永远停在null—— 于是后面找scrollBody/ 输入卡时不再限定在中间列内。这会连带影响玻璃化「压进对话区」的判定与内容带对齐。现在改由新增的getFrameEl()反查框架,实测能正确选中centerCol。 - 修复:侧栏收起检测。收起信号现在优先读框架上的
data-sidebar-collapsed(getFrameEl()先试旧属性以兼容 0.1.6 及以前,取不到则由侧栏列 / 中间列 / 右栏列反查父节点得到框架)。列宽兜底同时从w > 0 && w <= 96改为「框架已布局 + 列宽 ≤ 96px」:Windows 标题栏模式下收起列宽为 0(且整列 0×0,列宽兜底本就取不到),旧判断会漏掉;而「框架是否已布局」是为了区分「真收起」与「首帧尚未布局」。 - 修正插件清单字段:
dsh.client.inject里声明的@deepseek-ai/dsh-client-runtime在新版依赖树中已不存在,改为实际提供客户端sessions服务的@deepseek-ai/dsh-api-session-controller。该字段只是信息性依赖(dsh-client-modules对找不到的包静默忽略,所以旧值不会报错,只是过时)。
主进程侧的记账逻辑(会话事件读取、V4 会话格式、模型计价)经核对无需改动:0.1.7 的
dsh-token-meter取值方式与本插件一致,模型目录仍是deepseek-flash(V4.1-Flash)与deepseek-v4-pro。
本次更新(v1.6.0)
| 玻璃化前后(左:未开启 · 右:开启 —— 只有压进对话文字的那块变了) |
|---|
全新的玻璃化表现:开启后,浮窗压进「对话内容列」的那一块会变成灰色玻璃:
- 标题栏 / 底部「会话花费·余额」:稍微透明(可调);
- 中间区域的背景:完全透明 —— 露出底下被模糊的对话内容;
- 中间的仪表盘与文字:只保留明度(去掉色相与饱和度 = 灰阶),并且每个颜色的不透明度 = 它的明度(例如明度 80 → 不透明度 80%),所以不同颜色的透明程度各不相同。
只作用在压进对话区的那块矩形里:没有进入对话区的部分完全保持原样(连仪表盘的颜色都不变)。做法是在 SVG 滤镜内部按矩形裁切 —— 洞内用变换结果、洞外用原图,洞边按「边缘过渡」羽化。
6 个设置项(设置面板里,全部实时生效并记住):
设置 范围 默认 说明 磨砂强度 0–24px 9px 模糊半径,越大越糊 透明强度 0–100% 47% 中间白雾的浓度,越大越通透 边缘过渡 0–40px 14px 正常区 ↔ 玻璃区之间的过渡宽度(0 = 硬边) 标题/底部透明 0–60% 15% 标题栏与底部行的透明度(0% = 完全不透明) 内容不透明度 0–100% 100% 在明度之上再乘的系数:100% = 严格按明度,0% = 看不见(全程线性,每档都有效) 明度算法 Rec.709 / HSL / HSV Rec.709 用哪种「明度」定义灰度与不透明度 修复:拖动「内容不透明度」或切换「明度算法」时,重建滤镜会把洞口矩形重置,导致变换消失、要等下一秒才恢复(表现为「拉滑杆没反应 / 不渐变」);现在即时生效。顺带把该滑杆范围从 0–200% 收回到 0–100%。
本次更新(v1.5.3)
- 修复:会话花费长时间不动(记账游标越界):会话日志被压缩/重写后 seq 空间会变短,而账本里持久化的
cursor仍停在旧 seq 空间,于是snapshotEvents(cursor, 日志末尾)永远返回空数组,新事件再也折不进账本——表现为「会话花费」长时间不涨、点了归零后一直是 ¥0.00。现在检测到cursor > session.seq时自动重建该会话账目,并从当前日志重新回放。 - 修复:表盘指针过了午夜逆时针倒退:
dialPosOfHour()在 00:00–06:00 段方向写反(90 - 15t),指针到弧顶后又折回左端。改为夜间段与白天段同为「左端 → 弧顶 → 右端」单向扫过:夜间 18:00 左端 → 24:00 弧顶 → 次日 06:00 右端,白天 06:00 左端 → 12:00 弧顶 → 18:00 右端;06:00 与 18:00 两处为瞬时跳变。 - 设置面板底部标注当前版本号:宿主
/state下发version(读自package.json),设置面板最下方用 10px 次要色小字显示dsh-cost-gauge vX.Y.Z;取不到版本时整行隐藏。
本次更新(v1.5.2)
| 设置:磨砂强度 / 透明强度 | 磨砂前后(左:未开启 · 右:开启) |
|---|---|
- 磨砂强度 / 透明强度可调(设置面板里两个滑杆,实时生效并记住):
- 磨砂强度
0–24px:模糊半径,越大越糊(0 = 只保留通透的白雾); - 透明强度
0–100%:越大越通透、白雾越淡,越能看清底下的内容。
- 磨砂强度
- 标题栏不磨砂:磨砂只作用于标题栏以下,标题栏始终原样不透明 —— 方便一眼定位、随手拖动。
- 边缘改为渐变过渡:磨砂区与普通区之间不再是"一条线",而是随磨砂强度自动加宽(5–18px)的渐变;实现上把"洞口"拆成「横向补集 ∪ 纵向补集」两段渐变遮罩(De Morgan),磨砂层用同一形状做遮罩,两侧同步渐隐。
本次更新(v1.5.1)
| 同一位置对比:未开启(左) / 开启磨砂后(右) |
|---|
- 磨砂终于看得出来:磨砂层原来只有 5% 白色 +
blur(12px)——深色对话背景被模糊后还是同样的深色,等于看不出变化。现在改为乳白渐变 + 轻微提亮 + 内描边(blur(9px) saturate(1.2) brightness(1.12)):实测同一块背景区域亮度 29 → 83,而底下文字模糊后仍保留结构(能看出文字位置)。 - 找不到开关? 未开启磨砂时,只要浮窗压住对话文字(遮挡 ≥ 浮窗面积 35%),会提示一次「可在设置里开启『遮挡对话区时磨砂玻璃』」。
- 修复位置卡在左上角:窗口最小化 / 后台标签页里加载时视口宽高为 0,「钳制到窗口内」会把浮窗位置算成 (0,0) 并写进
localStorage;现在视口为 0 时不做钳制。
本次更新(v1.5.0)
| 设置:新增「遮挡对话区时磨砂玻璃」开关 |
|---|
- 窗口尺寸变化时自动让位:拉伸/缩放窗口后,如果浮窗压住了中间对话的文字区,会自动挪进「文字区 ↔ 侧栏」之间的留白:留白够宽就停在留白正中、靠下部分(贴窗口底部;若会压到输入框,则停在输入框正上方 8px),留白不够宽才退回侧栏那一列。
- 优先级:中间文字区(绝不遮挡)> 底部输入框 > 两侧侧栏;同分时保持「偏左往左、偏右往右」并尽量少移动。
- 只在窗口尺寸变化时触发(150ms 防抖),不干扰手动拖拽;竖版小条模式下不参与。
- 何时能停进留白:单侧留白需 ≥ 插件宽 + 16px。默认 216px 宽时约需窗口 ≥ 1658px(中间列 ≥ 1378px,侧栏 280 展开、右侧栏关闭);缩到最小 180px 时约需 1458px。
- 遮挡对话区时的局部磨砂玻璃(新增设置项,默认关闭):开启后,浮窗与对话文字区相交的那一块会变成半透明磨砂(
backdrop-filter),能看清被压住的文字;只磨砂真正遮挡文字的部分 —— 停在留白里不会变磨砂。实现为「背景层挖洞 + 磨砂层铺洞」(mask-composite: exclude)。 - 侧栏收起时自动停靠:DSH 自动收起侧栏(或视口很窄)时,竖版小条自动停到侧栏右侧;侧栏展开时停到「会话 / 工作区」标题下方;退出竖版时回到原来的位置(停靠位置不写入本地存储)。
- 竖版小条宽度 48px → 36px,更省空间;点一下仍可展开。
- 内部修正:让位与磨砂判定用的「对话内容区」改为按
--dsh-chat-content-width(面板内居中的内容列)计算,并正确解析clamp()/calc()表达式;取不到时依次退回「输入卡宽度 → 整个中间面板」,避免把面板两侧的留白误判成对话区。
本次更新(v1.4.0)
| 窗口变窄 → 竖版最小化 |
|---|
- 窗口自适应(竖版最小化):用
ResizeObserver观察视口宽度,≤ 779px(即 < 780px) 自动切换为 48px 宽的竖版最小化(自上而下:状态灯 → 会话花费 → 余额 → 模型徽标,均竖排),点击即展开;窗口回到 ≥ 860px 自动恢复之前的展开/折叠状态。- 带迟滞(进入 780 / 退出 860),避免在临界点反复抖动;手动点开过竖版后,本段窄窗口内不再自动收窄(窗口回到 860px 以上才重新启用)。
- 阈值可覆盖:
localStorage的dsh-cost-gauge:narrowEnter/dsh-cost-gauge:narrowExit。
本次更新(v1.3.0)
| 花费记录面板 | 设置项 | 表盘端头修正 |
|---|---|---|
- 费用计算口径修正:改为回放会话日志、按「事件发生时刻的费率 × 当时的模型」逐笔计价——空闲时段按空闲价、高峰时段按高峰价,两者相加;修掉了旧版"进入高峰后整段历史按高峰价重算(费用翻倍)"的问题。
- 新增「记录 / 归零」两个图标按钮(表盘左上/右上,无边框小图标,悬停显示提示):记录面板支持
总时间 / 年 / 月 / 周筛选与翻页、柱状图(峰谷/模型堆叠)、明细表格;明细只列有使用记录的时段;范围可切本会话 / 全部会话;面板可拖动、可覆盖指示器、越界自动拉回窗口内。归零为两步确认,把当前累计作为基线后从 ¥0.00 重新累计(不删历史)。 - 导出 Excel 升级为真正的
.xlsx(OOXML,零依赖手写 zip 写入器,Excel/WPS 直接打开无格式警告):两个工作表 + 首行导出说明;默认文件名<会话名称>_<起>-<止>.xlsx;可在设置里指定默认保存文件夹(已设置则直接落盘不弹窗),导出后📂 打开直接定位到文件。 - 宿主侧记账:每 15 秒回放会话事件日志并持久化到
~/.dsh/cost-gauge/ledger.json,仪表盘没打开时也在记;记录覆盖会话完整历史。 - 表盘端头修正:黄段(高峰)在内部边界(9:00 / 12:00 / 14:00)按圆头半径内缩 5.43°,圆头外缘正好压线、不再盖住相邻绿段;18:00 端头保持原来的圆头外观。
- 中英双语界面:跟随 DSH 客户端语言设置,取不到时跟随系统/浏览器语言(
zh*→ 中文,其余 → English)。
功能
- 🔲 方形浮动窗:默认停在界面左侧靠上,可按住标题栏拖动,位置自动记忆。
- 📱 窗口自适应:窗口变窄(视口 ≤ 779px,即 < 780px)时自动切为 36px 竖版最小化(灯 + 花费 + 余额 + 模型徽标),点击展开;窗口恢复(≥ 860px)自动还原;侧栏自动收起时竖版小条会停靠到侧栏右侧、侧栏展开时停到「会话 / 工作区」标题下方。
- 🪟 窗口变宽/变窄自动让位:窗口尺寸变化时,若浮窗压住中间文字区,自动挪进「文字区 ↔ 侧栏」之间的留白(正中靠下);优先级为 文字区 > 底部输入框 > 两侧侧栏。
- 🧊 遮挡对话区的玻璃化(设置里 6 个选项):压进对话文字的那块变成灰色玻璃 —— 标题栏/底部行半透明、中间背景全透明、仪表盘与文字按「不透明度 = 明度」灰化;没进入对话区的部分保持原样。
- 💰 会话花费:按官方峰谷价实时换算当前会话的 token 花费(缓存命中/未命中、输出分桶计价)。
- 逐笔按当时费率计价:空闲时段产生的用量按空闲价、高峰时段产生的用量按高峰价,两者相加;进入高峰不会把之前的空闲用量也按高峰价重算。
- 🧮 本回合花费:只统计当前回合 —— 回合一开始归零重算,与会话花费走同一套逐笔计价,所以两个数字口径一致;不足 1 分显示
<0.01。- 紧凑态按需切换:设置里的「最小化与竖条显示本回合花费」决定最小化胶囊和贴边竖条显示哪一个(默认仍显示会话花费)。
- 🧭 费率指针:指针摆向「标准(空闲)」或「翻倍(高峰)」,并显示距下一次切换的倒计时。
- 高峰(翻倍):北京时间周一至周五 09:00–12:00、14:00–18:00
- 空闲(标准):其余时间(含周六、周日全天),价格为高峰的一半
- 🔴 红灯报警:余额低于阈值(默认 ¥10)时,窗口顶部小红灯闪烁报警;余额充足时绿灯。
- ⚙️ 阈值可设:点齿轮即可改报警阈值,立即生效并记住(localStorage)。
- 🗒 每日花费记录(表盘左上角图标):宽面板展示花费记录,支持 总时间 / 年 / 月 / 周 时间筛选与翻页、柱状图(按峰谷或按模型堆叠)、明细表格与合计;范围可切 本会话 / 全部会话;明细只列有使用记录的时段(柱状图仍保留完整时间轴);面板可拖动、可覆盖指示器,越界会自动拉回窗口内。
- ⬇ 导出 Excel:导出当前筛选范围为真正的
.xlsx(OOXML;手写最小 zip 写入器,零第三方依赖,Excel/WPS 可直接打开、无格式警告),含 2 个工作表:按峰谷拆分、按模型拆分;两个表的首行是导出说明(会话名称、计费时间段、时间筛选、上次归零、导出时间);默认文件名 =<会话名称>_<起>-<止>.xlsx(如花费指示器插件_20260808-20260911.xlsx,全部会话则为全部会话_...);导出成功后可点📂 打开在资源管理器中定位文件。 - 📁 默认保存位置:在设置里指定文件夹后,导出直接落盘、不弹窗;未设置时会弹原生「选择文件夹」对话框并把所选目录记为默认(设置持久化在
~/.dsh/cost-gauge/ledger.json)。 - ↺ 会话花费归零(表盘右上角图标):两步确认后把当前累计作为基线,之后从 ¥0.00 重新累计(不删除历史记录)。
- 🧮 宿主侧记账(日志回放):宿主每 15 秒回放各会话的事件日志(
assistant/message/assistant/attempt自带 usage 与时间戳,request/header提供当时的模型),按「事件时刻的费率 × 当时的模型」逐笔计入「日期 × 峰谷 × 模型」并持久化到~/.dsh/cost-gauge/ledger.json;llm/retry-started与相同 turn/step 的重复样本按官方投影口径替换而非累加。因此仪表盘没打开时也在记,且记录覆盖会话日志里的完整历史(不限于插件安装之后)。 - 🌐 中英双语界面:跟随 DSH 客户端语言(设置 → 通用 → 语言),取不到时跟随系统/浏览器语言(
zh*显示中文,其余显示英文)。
安装
一键安装(推荐,无需 git)
PowerShell 复制整行回车(自动补齐 dsh,无需本机 git):
irm https://raw.githubusercontent.com/wjingshan/dsh-cost-gauge/main/install.ps1 | iex
一键安装自动装最新稳定版(GitHub 最新 Release tag,发版后无需改脚本)。想装开发版或锁指定版本,先下载脚本再带参数运行:
irm https://raw.githubusercontent.com/wjingshan/dsh-cost-gauge/main/install.ps1 -OutFile install-dsh-cost-gauge.ps1 .\install-dsh-cost-gauge.ps1 -Ref main # 装 main 开发版 .\install-dsh-cost-gauge.ps1 -Ref v1.5.0 # 锁指定版本
仓库尚未推送时可先用本地脚本装(-Source 指定本地目录):
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Source .\dsh-cost-gauge
手动安装
插件市场(GitHub
dsh-plugin索引 / awesome-dsh-plugin.com)里给的那条安装命令,就是下面第一条github:wjingshan/dsh-cost-gauge。它属于 git 安装,取的是main分支的最新提交,与 GitHub Release 无关 —— 所以「正式版 / 开发版」之分只对上面的一键安装有意义:install.ps1认的是/releases/latest,市场认的是分支最新提交。
# 推荐:跟 main 分支 —— 插件市场能正常提示并执行「更新」
dsh plugin --profile web add github:wjingshan/dsh-cost-gauge#main
# 从本地目录安装(链接方式,改 lib/*.js 后刷新页面即生效)
dsh plugin --profile web add link:/path/to/dsh-cost-gauge
# 锁版本(不推荐,见下方说明:市场不会自动更新)
dsh plugin --profile web add github:wjingshan/dsh-cost-gauge#v1.6.0
dsh plugin --profile web add https://github.com/wjingshan/dsh-cost-gauge/archive/refs/tags/v1.6.0.tar.gz
⚠️ 关于插件市场的「更新」:市场判断 git 安装能不能更新,看的是 commit 有没有变,不是版本号。
- ✅ 想让市场能更新:装分支写法(
#main,或省略 ref 的github:owner/repo)。- ❌ 别装成固定 tag(
github:owner/repo#v1.6.0):市场的更新命令会把同一个 tag 再装一遍,于是必然报「更新命令执行完成,但版本没有变化,原因未能确认」——点重试也不会成功,只能手动改 spec。- 用 Release tarball 直链安装的市场不会提示更新(锁文件里没有可比较的 commit),需要更新时手动重装。
装完重启 dsh web,刷新页面即可看到左上角浮动窗。
dsh web
配置
余额阈值既可在浮动窗里点齿轮改,也可在 profile 的 cordis.patch.yml 里覆盖:
- update:
- id: cost-gauge
config:
threshold: 10 # 余额报警阈值(人民币)
baseUrl: 'https://api.deepseek.com'
apiKeyEnv: 'DEEPSEEK_API_KEY'
refreshSeconds: 30 # 余额查询缓存秒数
覆盖时需完整重述该行需要的全部 config 键(patch 按行整体替换 config,不做深合并)。
发布新版本
改完代码后,用 release.ps1 一条命令完成:提交 → 升版本 → 推送 → 创建 GitHub Release。
.\release.ps1 -Message "feat: 新增 xxx" # 默认 patch(1.0.0 → 1.0.1)
.\release.ps1 -Type minor -Message "feat: 新增 xxx" # minor(→ 1.1.0)
.\release.ps1 -Version 1.2.0 -Message "feat: 新增 xxx" # 显式版本号
.\release.ps1 -Message "..." -DryRun # 预演(不真正执行)
- Release 说明默认从「上一个 tag 以来的提交历史」自动生成,也可
-Notes "…"自定义。 - 创建 Release 需要 PAT:设置环境变量
GH_TOKEN(fine-grained,仓库权限 Contents 读写),或运行时按提示输入。 - 发版后无需改任何脚本——
install.ps1会自动安装最新 Release tag。 release.ps1内部会git add -A并提交,发版前先git status看一眼,别让临时截图 / 调试脚本跟着提交进去。
⚠️ 推了 tag 不等于发布了版本。
install.ps1取的是 GitHub 的 Release(/releases/latest),不是最新的 tag。(插件市场给的是dsh plugin --profile web add github:wjingshan/dsh-cost-gauge——那是 git 安装,取默认分支最新提交,与 Release 无关。)所以只git push --tags而没建 Release,走install.ps1一键安装的人会静默停在旧版上——v1.6.1 ~ v1.6.3 就踩过这个坑:三个 tag 都在远端,Release 却还停在 v1.6.0,直到补建才对齐。发完版确认三处对齐(tag / Release /
main):gh release list --limit 3 # 第一行应是刚发的 tag,且标着 Latest gh api repos/wjingshan/dsh-cost-gauge/releases/latest --jq .tag_name # 应与刚发的 tag 一致漏建了可以补(只是给已存在的 tag 挂上 Release,不动任何提交):
gh release create v1.6.3 --repo wjingshan/dsh-cost-gauge --title v1.6.3 --notes-file notes.md
gh需要已登录(gh auth login);也可以沿用脚本里的 PAT 方式,直接POST /repos/wjingshan/dsh-cost-gauge/releases并带上tag_name。
数据与安全
- 余额经官方
GET /user/balance查询,API Key 只在宿主侧解析(credentials 接缝 / 环境变量),绝不下发浏览器。 - 花费由宿主读取会话的
tokenUsage投影、按官方峰谷价换算;缓存写入不单独计费(与官方口径一致)。 - 纯 ESM、零运行时依赖:宿主不 import 任何包,浏览器半身是原生 JS(无 React)。
目录结构
dsh-cost-gauge/
├── package.json # dsh.bundle(宿主)+ dsh.client(浏览器)声明
├── cordis.patch.yml # 插件行插入(含默认 config)
├── install.ps1 # 一键安装脚本(irm … | iex)
├── release.ps1 # 一键发布脚本(提交+升版本+推送+创建 Release)
├── docs/ # 截图 / 预览页 / 支付宝收款码(README 引用)
├── lib/
│ ├── index.js # 宿主半身:余额查询 + 花费统计 + 峰谷判定 + /api/cost-gauge/* 路由
│ ├── ledger.js # 宿主半身:会话事件记账、账本持久化、XLSX 导出(零依赖)
│ ├── holidays.js # 中国法定节假日表(2026)+ 调休补班日,供峰谷判定使用
│ └── client.js # 浏览器半身:方形浮动窗(指针表 + 红灯 + 拖动 + 边缘吸附 + 阈值设置)
└── README.md / README.en.md
反馈与建议
用得顺手、有想法,或者踩到坑了,都欢迎到 Issues 说一声——我会尽量回复。
- 🐛 报 Bug:附上 dsh 版本、插件版本和复现步骤;有截图最好(浮动窗截图最直观)。
- 💡 提建议 / 新功能:直接开 issue 描述使用场景即可,不必先写实现方案。
- ⭐ 觉得好用:点个 Star 就是最实在的支持,也能让更多人看到这个插件。
想直接提交代码的话,建议先开个 issue 对齐思路再发 PR,避免白做。
License
MIT
☕ 赞助
如果这个插件帮到了你,欢迎请我喝杯咖啡 ☕
感谢你的支持! 💙
评论
评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。