メインコンテンツへスキップ
X

dsh-rail

xiaoxiaolelang-ai/dsh-rail

Narrow 40px icon rail for the DSH sidebar: panel icons move into a single column so the session list gets the full width, session titles gain a three-level hierarchy, and the DeepSeek account balance sits just above the sidebar footer with value-based colours.

インストール

dsh plugin --profile web add github:xiaoxiaolelang-ai/dsh-rail

README

窄轨 · dsh-rail

给 DeepSeek Harness 的侧边栏做减法:把面板图标收进一条窄栏,把宽度还给会话列表,把工作区与会话分出层次,并在底部栏上方常驻一条 DeepSeek 余额。

功能

  • 图标窄栏。 侧栏改成「40px 图标列 + 内容列」两列栅格:所有面板图标进左边竖栏,品牌、新建会话、工作区与会话列表占右边整列。工作区不再被挤在下半栏,会话标题能拿到完整宽度。图标悬停有原生 title 提示,当前面板左侧有一条 2px 强调条。
  • 侧栏层次。 工作区之间是一条 1px、强调色淡染的圆头分隔线;组内会话之间是一条 1px 中性发丝线;每个工作区下面拉一条淡竖线,把它自己的会话括起来;选中会话左侧 2px 强调条,键盘聚焦时未选中的行也露出同一条。工作区标题字重 500、文件夹图标提到二级色。细、淡、圆头——层次出来了,但不抢内容。
  • 会话数。 工作区行末尾显示它下面有几个会话(11px、三级色、等宽数字),悬停时让位给行操作按钮。
  • 会话标题层次。 普通会话次级色,选中 / 悬停 / 键盘聚焦升到主色,选中再加一档字重。相对时间保持三级色。
  • 滚动条边距。 会话列表右缘按原生滚动条宽度 + 偏移内缩,行 hover 底色和滚动条对齐。
  • 余额条。 侧栏底部栏正上方一行:状态圆点 + ¥ 23.41 + 今日 0.42 + 累计 3.70(小标签 + 数值,右对齐成组,中间一条竖线)。圆点四档:正常中性灰、低于 50 琥珀、低于 10 红、未登录暗灰;数字本身也按同样阈值变色。平时每 60 秒自动刷一次,切回窗口时也会刷。
  • 点一下去充值。 点击余额条直接在新窗口打开平台充值页——地址来自宿主账号状态(deepseekAccount.getState().links.topUpUrl),不是插件里写死的。Shift + 点击才是立即刷新;宿主没给地址时,点击退回刷新。悬停看充值 / 赠金分项、更新时间、今日与累计消费(含累计起始日)、今日充值 / 今日赠金。
  • 跟随主题。 零处硬编码颜色,全部走 --dsw-alias-* 主题令牌(包括线条的 alpha 叠加),浅色 / 深色自动跟随。

会话数是怎么来的

展开状态下数的是屏幕上真实的会话行;折叠状态下宿主根本不渲染那些行,所以用的是上次展开时记下的值(localStorage,键 dsh-rail:session-counts:v2)。

  • 展开过一次之后,折叠状态也会一直显示;
  • 首次启动、且该工作区处于折叠状态时不显示数字——插件不猜,也不去读宿主内部状态;
  • 某个工作区在别处增删了会话,折叠时显示的是旧值,等它下次展开才会纠正。

今日花费是怎么算的

右侧那个 ↓1.50 是今日累计消费,不是"当前余额和早上比差了多少"。

  • 只用分钱包采样(充值余额、赠金余额各算各的)。原先读写 DSH_HOME/dsh-usage/usage-ledger.json 的"账本优先"路径已移除:已经没有插件在写那个文件,读它只会显示过期数字。合并成总余额做差是不行的:一次净涨既可能是充值、也可能是赠金到账,事后拆不开——上一版正是因此把「充值 10 元」显示成了别的数。
    • 任一钱包余额下降 → 记进今日消费(不管从哪个钱包扣的);
    • 充值钱包上升 → 记进今日充值;
    • 赠金钱包上升 → 记进今日赠金;
    • 因此充值之后今日消费既不会变负、也不会被冲掉。
    • 累计消费 = 已完成日期的合计 + 今天;换日时把当日消费并入累计并持久化(状态文件 v: 3,从 v2 迁移时把已有当日值当作起点,不凭空造历史)。
    • 余额掉了 → 把降幅加进今日消费,基准移到新余额;
    • 余额涨了(充值 / 赠金)→ 不动已经累计的消费,只把基准抬高,涨幅另外记进 toppedUpToday;
    • 因此充值之后今日消费不会变负数、也不会被冲掉。
  • 基准和累计值落在 DSH_HOME/dsh-rail/day-spend.json,重启当天接着算;跨天自动归零。
  • 精度取决于 60 秒的轮询间隔:同一分钟内"先充值后消费"会被当成净涨,那点误差不值得更复杂的记账。
  • 今天还没花到一分钱就不显示数字;悬停能看到今日花费与今日充值。

累计消费的口径(重要)

  • 这个数字不是账号的历史总消费:官方余额接口只给三个余额(总额 / 充值 / 赠金),反推不出历史。
  • 它只覆盖插件持续运行期间观察到的余额下降;DSH 关闭或插件被禁用期间的消耗会漏记。
  • 就算你在平台网页直接调 API,只要插件在跑,余额下降仍会被记到。

安装

插件管理器里添加 dsh-rail,或者:

dsh plugin install dsh-rail

安装后刷新页面(首次安装或升级客户端代码时,正常退出 Desktop 再启动最稳妥)。

余额从哪来

  • 优先用宿主内置的 DeepSeek 账号(deepseekAccount 服务)。已登录就能显示,不需要 API Key。
  • 账号不可用时回退到 DEEPSEEK_API_KEY:先读环境变量,再读宿主凭据记录。明文只在 Host 进程内使用,不会下发到浏览器。这条请求带 10 秒超时,不会让余额条卡在「读取中」。
  • 两条都拿不到时显示「未登录」,不编造数字。

凭据只存在 Host 侧,浏览器通过 connection 的精确 fetch 路由(/api/dsh-rail/balance)取已算好的 JSON。

工作原理

不替换任何 slot、不重排 React 树、也不追加结构性 DOM 节点:

  • 原生类名运行时从注入的 CSS Module(style[data-plugin-css="…"])里解析,宿主升级换哈希也不影响。
  • 结构样式用到的类名单独解析:解析不到只跳过那几条规则,窄栏与余额照常工作。
  • 会话数只写在行自己的 data 属性上,由 CSS content: attr() 渲染,不插节点;卸载时逐个删掉。
  • 卸载时移除自己注入的样式表、监听器、title 属性与计数属性,全部还原。

兼容与边界

  • 声明范围 engines.dsh >= 0.2.2-rc.1(在 0.2.2-rc.1 上验证)。
  • 侧栏折叠成 56px 时,宿主自己会隐藏整个底部区,余额条跟着隐藏——这是原生行为,本插件不去覆盖。
  • 线条用「主文字色 + alpha」叠色,深色主题下取值更高,因为同样的 alpha 在深底上更难认。
  • 没有做工作区吸顶:position: sticky 要求行与滚动容器之间没有 overflow 裁剪,而宿主的行容器不受本插件控制;做了会出现「有时吸、有时不吸」,比不做更糟。
  • 与修改侧栏结构或全局主题的其他皮肤插件可能互相覆盖样式。
  • 与繁界(fanframe)同属侧栏外观类插件,建议二选一。

更新记录

  • 0.2.2 —— 余额条点击改为打开平台充值页(地址取自宿主账号状态),Shift + 点击保留「立即刷新」。
  • 0.2.1 —— 今日花费改成累计消费:充值不再把数字冲成负数或让它消失;充值额单独记录并在悬停里显示。
  • 0.2.0 —— 侧栏层次(组间 / 会话间分隔线、组竖线、选中条、键盘焦点条)、工作区会话数、余额状态圆点与今日净变化;修复 Key 兜底请求无超时会让余额条永久停在「读取中」。
  • 0.1.0 —— 图标窄栏、会话标题层次、滚动条边距、余额条。

开发

node --check client.js        # 客户端语法
node _smoke.mjs               # 假 ctx 跑一遍 apply,打印最终注入的 CSS(29 项)
node _test-degrade.mjs        # 拖拽类名缺失时结构层仍在(2 项)
node _test-counts.mjs         # 四种 DOM 形状下的会话计数(4 项)
node _smoke-host.mjs          # Host 半边取数与今日花费(6 项)
npm pack                      # 生成可分发的 tarball

阈值在 client.js 顶部:WARN_AT = 50、LOW_AT = 10;窄栏宽度在 railCss() 里。

License

MIT


dsh-rail (English)

A small sidebar plugin for DeepSeek Harness: panel icons move into a narrow 40px rail so the session list gets the whole width, workspaces and sessions gain a quiet visual hierarchy, and a DeepSeek balance line sits just above the sidebar footer.

  • Icon rail. Two-column grid (40px icon column + content column). Panel icons stack vertically; brand, new session, workspaces and the session list keep the full right column. Icons get a native title tooltip, and the active panel gets a 2px accent bar. No structural DOM is added, moved or replaced.
  • Sidebar hierarchy. A 1px rounded divider tinted with the accent colour between workspaces; a 1px neutral hairline between sessions inside a workspace; a faint vertical guide under each workspace header; a 2px accent bar on the selected session, reused for keyboard focus on any other row. Workspace titles get a 500 weight and their folder icon one step more contrast.
  • Session count. Each workspace row ends with how many sessions sit under it — hidden while hovering so the row actions have room.
  • Title hierarchy. Idle sessions use the secondary label token; selected, hovered and keyboard-focused rows use the primary token; the selected row gains one weight step.
  • Balance line. Above the footer: a status dot, ¥ 23.41, and today's net change. The dot is neutral when healthy, amber below 50, red below 10, grey when signed out. Click to refresh now; polls every 60 seconds and on window focus.
  • Themed. No literal colours — only --dsw-alias-* tokens, alpha layering included, so light and dark follow the host.

Where the session count comes from: expanded workspaces are counted from the rows actually on screen; collapsed ones reuse the last value seen while expanded (localStorage, key dsh-rail:session-counts:v2). Nothing is shown for a workspace that has never been expanded in this profile — the plugin does not guess and does not read host internals.

Today's spend prefers the usage ledger at DSH_HOME/dsh-usage/usage-ledger.json; when that has no entry for today it falls back to the balance delta, with the day's baseline persisted at DSH_HOME/dsh-rail/day-spend.json so a restart does not reset it. Positive means spent today, negative means topped up or granted.

The balance comes from the host's deepseekAccount service (no API key needed when signed in), falling back to DEEPSEEK_API_KEY with a 10 second timeout. Credentials never reach the browser; the client reads a pre-computed JSON document over an exact connection fetch route.

Requires DSH >= 0.2.2-rc.1. When the sidebar collapses to its 56px rail the host hides the footer, so the balance line hides with it. Sticky workspace headers are deliberately not implemented: position: sticky needs no overflow clipping between the row and the scroll container, and the host's row container is outside this plugin's control.

MIT.

関連プラグイン