跳到正文
dsh-market 浏览插件 GitHub EN

wjingshan/dsh-cost-gauge

峰谷计费表盘:DSH Web 浮动窗按 DeepSeek 官方峰谷(高峰/空闲)费率对会话花费(token 费用)实时计费,指针配费率切换倒计时,余额环带低余额报警,支持拖拽缩放与最小化状态灯。

Star 数 ★ 1 分类 用量与计费 收录于 2026-09-06

安装

在 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(),按可靠性依次尝试,任一成功即用):
    1. localStorage['dsh.sessions.current'] —— DSH 自己持久化的选择(形如 { sessionId })。dsh-client-store 的 attachPersistence 在每次状态变化时同步写回,所以它是实时信号,不是陈旧缓存。
    2. [data-sidebar-right-session]:not([hidden]) —— 右栏当前会话元素,同样由 DSH 自己写入。
    3. 旧版快照的 .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


☕ 赞助

如果这个插件帮到了你,欢迎请我喝杯咖啡 ☕

感谢你的支持! 💙

内容来自项目 README(GitHub)↗

评论

评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。