dsh-desktop-statusbar 2.2.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +13 -1
- package/README.md +13 -1
- package/cordis.patch.yml +10 -1
- package/docs/pricing.en.png +0 -0
- package/docs/settings.en.png +0 -0
- package/docs/settings.png +0 -0
- package/docs/statusbar.en.png +0 -0
- package/docs/statusbar.png +0 -0
- package/lib/balance-api.js +68 -4
- package/lib/client.js +3013 -298
- package/lib/holiday.js +39 -6
- package/lib/index.js +5622 -4216
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +2 -2
- package/tools/shot.cjs +33 -11
package/README.en.md
CHANGED
|
@@ -22,7 +22,7 @@ Replaces the stats line under the message input in the DSH desktop app with a co
|
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
25
|
-
- **
|
|
25
|
+
- **9 optional fields plus the context ring**: context occupancy (the ring at the head of the bar), peak / off-peak check, turns & steps, cache hit rate, output speed, TTFT, run time, this turn cost, session cost, balance
|
|
26
26
|
- **Click a field for details**:
|
|
27
27
|
|
|
28
28
|
| Field | Content |
|
|
@@ -73,6 +73,18 @@ Both ways above install from the npm registry; the package name is `dsh-desktop-
|
|
|
73
73
|
npm view dsh-desktop-statusbar version
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### When the built-in updater fails
|
|
77
|
+
|
|
78
|
+
The one-click update button inside the plugin can fail on some desktop builds — it either does nothing or reports a failure. Do not keep clicking it. Use the official install entry instead and **append `@version` to the package name** to pin the exact version:
|
|
79
|
+
|
|
80
|
+
```powershell
|
|
81
|
+
dsh plugin --profile desktop add dsh-desktop-statusbar@3.0.0
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The same string works in the plugin panel — type `dsh-desktop-statusbar@3.0.0` into the **Add plugin** field.
|
|
85
|
+
|
|
86
|
+
Use whatever version you see (the version on the release page, or the output of the `npm view` command above). Pinning the version has a second benefit: **you get exactly the build you asked for**, with no chance of a stale cached package.
|
|
87
|
+
|
|
76
88
|
Also listed on [dshmarket](https://awesome-dsh-plugin.com/p/raphael-y7/dsh-desktop-statusbar/), [dshfind](https://dshfind.com/zh/plugins/raphael-y7/dsh-desktop-statusbar) and [1024Store](https://deepseek1024.com/plugins/Rapheal-Y7/dsh-desktop-statusbar).
|
|
77
89
|
|
|
78
90
|
## Usage
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
## 功能
|
|
24
24
|
|
|
25
|
-
- **
|
|
25
|
+
- **9 个可选字段 + 栏首上下文圆环**:上下文占用(栏首圆环)、峰谷判断、轮次&步数、缓存命中率、输出速度、首字平均、运行用时、本轮费用、总计费用、余额
|
|
26
26
|
- **点击字段可查看详情**:
|
|
27
27
|
|
|
28
28
|
| 字段 | 内容 |
|
|
@@ -73,6 +73,18 @@ dsh plugin --profile desktop add dsh-desktop-statusbar
|
|
|
73
73
|
npm view dsh-desktop-statusbar version
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### 插件自带的更新失效时
|
|
77
|
+
|
|
78
|
+
插件里那个一键更新按钮,在某些桌面端版本上会失败(点了没反应、或者直接显示失败)。这时不要反复点,改用官方安装入口、**在包名后面加上 `@版本号`** 指定到具体版本,就能精确装上:
|
|
79
|
+
|
|
80
|
+
```powershell
|
|
81
|
+
dsh plugin --profile desktop add dsh-desktop-statusbar@3.0.0
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
插件面板里同样可以这样填 —— 在「添加插件」的输入框里写 `dsh-desktop-statusbar@3.0.0`。
|
|
85
|
+
|
|
86
|
+
版本号填你看到的最新版本即可(发布页右侧的版本号,或上面那条 `npm view` 命令的输出)。带上版本号还有一个好处:**装上的就是你想要的那一版**,不会因为缓存拿到旧包。
|
|
87
|
+
|
|
76
88
|
本插件亦收录于 [dshmarket](https://awesome-dsh-plugin.com/p/raphael-y7/dsh-desktop-statusbar/)、[dshfind](https://dshfind.com/zh/plugins/raphael-y7/dsh-desktop-statusbar) 与 [1024Store](https://deepseek1024.com/plugins/Rapheal-Y7/dsh-desktop-statusbar)。
|
|
77
89
|
|
|
78
90
|
## 使用
|
package/cordis.patch.yml
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# dsh-desktop-statusbar bundle 层
|
|
2
|
+
#
|
|
3
|
+
# 一个包一个组件。开发模式是主插件的一部分,**没有独立开关** ——
|
|
4
|
+
# 它只是作者调试用的入口(底栏吃假数据 + 连点版本号进出),日常不出现,
|
|
5
|
+
# 也不会在界面上多出任何可见控件。
|
|
6
|
+
#
|
|
7
|
+
# 曾经拆成过第二个组件(插件管理页单独开关),已经回退。原因:单包只能声明
|
|
8
|
+
# 一份客户端入口、client.js 是单份打包的,关掉组件**不会**卸载那部分代码 ——
|
|
9
|
+
# 于是"关掉"只能靠客户端运行时探测/轮询去管控,名不副实。
|
|
10
|
+
# 拆成多个 npm 包才能真正隔离,但那个代价不值得。
|
|
2
11
|
- insert:
|
|
3
12
|
- id: dsh-desktop-statusbar
|
|
4
13
|
name: 'dsh-desktop-statusbar'
|
|
5
|
-
config: {}
|
|
14
|
+
config: {}
|
package/docs/pricing.en.png
CHANGED
|
Binary file
|
package/docs/settings.en.png
CHANGED
|
Binary file
|
package/docs/settings.png
CHANGED
|
Binary file
|
package/docs/statusbar.en.png
CHANGED
|
Binary file
|
package/docs/statusbar.png
CHANGED
|
Binary file
|
package/lib/balance-api.js
CHANGED
|
@@ -11,6 +11,26 @@
|
|
|
11
11
|
* { currency: 'CNY' | 'USD' | ..., total: '12.34', parts: [{ label, amount }] }
|
|
12
12
|
* currency 缺失时由调用方按站点/域名推断;parts 用来填气泡里的构成行(充值 / 赠金等)。 */
|
|
13
13
|
|
|
14
|
+
/* 积分套餐的上游数据上限(见下面 workbuddy 的 parse)。
|
|
15
|
+
* 名字长度限制的是下游 creditDisplayName 的前缀剥离开销(O(n²)):实测 500KB 名字
|
|
16
|
+
* 会让浏览器主线程卡 18 秒。200 字符远大于任何真实包名(最长的也只有 16 字)。
|
|
17
|
+
* 条数限制的是渲染开销:这个列表在底栏的每秒重渲染路径上,没有 memo。 */
|
|
18
|
+
const CREDITS_NAME_MAX = 200;
|
|
19
|
+
const CREDITS_MAX_PACKAGES = 256;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 字符串的短哈希(base36,约 6~7 位),O(n)、无依赖。
|
|
23
|
+
* 只用于给截断后的超长包名加一个「不同原名不会碰撞」的标记 —— 不是密码学哈希,
|
|
24
|
+
* 也不需要抗碰撞攻击:这里要防的是「两个正常上游数据被截成同名」,不是恶意构造。
|
|
25
|
+
*/
|
|
26
|
+
function nameHash(text) {
|
|
27
|
+
let hash = 5381;
|
|
28
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
29
|
+
hash = ((hash * 33) ^ text.charCodeAt(i)) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
return hash.toString(36);
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
const adapters = [
|
|
15
35
|
{
|
|
16
36
|
id: 'deepseek',
|
|
@@ -27,12 +47,37 @@ const adapters = [
|
|
|
27
47
|
const infos = Array.isArray(data && data.balance_infos) ? data.balance_infos : [];
|
|
28
48
|
if (infos.length === 0) return null;
|
|
29
49
|
const one = infos[0];
|
|
50
|
+
/* 数组元素可能是 null(上游坏数据):直接读 total_balance 会抛 TypeError,
|
|
51
|
+
整条余额就永久变横杠,所以先挡一道。 */
|
|
52
|
+
if (one === null || one === undefined || typeof one !== 'object') return null;
|
|
30
53
|
const total = typeof one.total_balance === 'string' ? one.total_balance : null;
|
|
31
54
|
if (total === null || total.length === 0) return null;
|
|
32
55
|
const parts = [];
|
|
33
56
|
if (typeof one.topped_up_balance === 'string') parts.push({ label: 'toppedUp', amount: one.topped_up_balance });
|
|
34
57
|
if (typeof one.granted_balance === 'string') parts.push({ label: 'granted', amount: one.granted_balance });
|
|
35
|
-
|
|
58
|
+
/* **额外**返回一份分币种余额:DeepSeek 的 balance_infos 里可能同时有 CNY 和 USD 两笔,
|
|
59
|
+
而上面那段(取 infos[0])是历史逻辑,老调用方还在用,语义一个字不能动。
|
|
60
|
+
这里遍历**全部**条目,让底栏能显示成「余额 ¥14.12+$3.17」。
|
|
61
|
+
只收 total > 0 的:0 与负数都不进(用户拍板「负数也不显示」,与 0 同样处理)。
|
|
62
|
+
币种只认 USD / CNY 两种,其余一律按 CNY(与文件其它地方口径一致),
|
|
63
|
+
这样上层不用再猜符号。 */
|
|
64
|
+
const balances = [];
|
|
65
|
+
for (const info of infos) {
|
|
66
|
+
if (info === null || info === undefined || typeof info !== 'object') continue;
|
|
67
|
+
const amount = typeof info.total_balance === 'string' ? Number(info.total_balance) : Number.NaN;
|
|
68
|
+
if (!Number.isFinite(amount) || amount <= 0) continue;
|
|
69
|
+
balances.push({
|
|
70
|
+
currency: info.currency === 'USD' ? 'USD' : 'CNY',
|
|
71
|
+
total: info.total_balance,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
currency: typeof one.currency === 'string' ? one.currency : 'CNY',
|
|
76
|
+
total,
|
|
77
|
+
parts,
|
|
78
|
+
/* 老调用方不看这个字段;上层拿到就用,拿不到(没有 balances 键)就退回单币种 */
|
|
79
|
+
balances: balances.length === 0 ? undefined : balances,
|
|
80
|
+
};
|
|
36
81
|
}
|
|
37
82
|
},
|
|
38
83
|
{
|
|
@@ -194,6 +239,9 @@ const adapters = [
|
|
|
194
239
|
let percent = null;
|
|
195
240
|
for (let i = 0; i < limits.length; i += 1) {
|
|
196
241
|
const one = limits[i];
|
|
242
|
+
/* 数组里可能是 null(上游坏数据):直接读 one.type 会抛 TypeError,
|
|
243
|
+
整条余额就永久变横杠了,所以先挡一道。 */
|
|
244
|
+
if (one === null || one === undefined || typeof one !== 'object') continue;
|
|
197
245
|
const kind = typeof one.type === 'string' ? one.type.toUpperCase() : '';
|
|
198
246
|
if (kind !== 'TOKENS_LIMIT' && kind !== 'CREDIT_LIMIT') continue;
|
|
199
247
|
const value = Number(one.percentage);
|
|
@@ -249,7 +297,11 @@ const adapters = [
|
|
|
249
297
|
const packages = Array.isArray(credits.packages) ? credits.packages : null;
|
|
250
298
|
if (packages === null) return out;
|
|
251
299
|
const rewards = [];
|
|
252
|
-
|
|
300
|
+
/* 条数上限:上游返回多少条不由我们决定,而列表渲染在底栏的每秒重渲染路径上
|
|
301
|
+
(无 memo),几万条会把界面拖死(实测 10 万条 ≈ 0.7 秒/帧)。
|
|
302
|
+
真实账号最多二三十个包,256 足够宽松,超出的直接忽略。 */
|
|
303
|
+
const count = Math.min(packages.length, CREDITS_MAX_PACKAGES);
|
|
304
|
+
for (let i = 0; i < count; i += 1) {
|
|
253
305
|
const one = packages[i];
|
|
254
306
|
if (one === null || one === undefined || typeof one !== 'object') continue;
|
|
255
307
|
const size = Number(one.size);
|
|
@@ -257,8 +309,20 @@ const adapters = [
|
|
|
257
309
|
/* size/remain 缺一个就没法算「已用/总量」:跳过这个包,
|
|
258
310
|
不让一条坏数据把整条气泡弄崩(旧插件/上游漂移都可能缺字段)。 */
|
|
259
311
|
if (Number.isFinite(size) !== true || Number.isFinite(remain) !== true || size < 0) continue;
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
312
|
+
const rawName = typeof one.packageName === 'string' ? one.packageName : '';
|
|
313
|
+
if (rawName.length === 0) continue;
|
|
314
|
+
/* 包名长度上限:上游是一条外部 API 响应,名字多长不由我们决定。
|
|
315
|
+
下游 creditDisplayName 要按前缀反复剥,超长名字是 O(n²)(实测 500KB 名字
|
|
316
|
+
能让浏览器主线程卡 18 秒);标签行也放不下。200 字符足够覆盖任何真实包名
|
|
317
|
+
(最长的「CodeBuddy个人版国内运营裂变包」也只有 16 字)。
|
|
318
|
+
**截断必须保证不同原名不碰撞 + 保持截断开销 O(n)**:客户端会把「显示名相邻
|
|
319
|
+
且相同」的包合并成 ×N 一行,若两个前缀相同、后缀不同的超长包被截成同名,
|
|
320
|
+
就会被误合并、显示伪造的合计(防御代码自己制造错误数据)。
|
|
321
|
+
只用「原始长度」不够 —— 长度相同的两个不同名字照样碰撞,所以带上一段
|
|
322
|
+
由全文算出的短哈希。 */
|
|
323
|
+
const name = rawName.length > CREDITS_NAME_MAX
|
|
324
|
+
? rawName.slice(0, CREDITS_NAME_MAX) + '…(' + rawName.length + ':' + nameHash(rawName) + ')'
|
|
325
|
+
: rawName;
|
|
262
326
|
const monthly = one.monthly === true;
|
|
263
327
|
rewards.push({
|
|
264
328
|
name,
|