@dougen/dsh-deepseek-usage 0.3.0 → 0.3.2
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.md +2 -2
- package/lib/client.js +44 -41
- package/lib/index.js +11 -9
- package/package.json +1 -1
- package/screenshot.png +0 -0
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ DeepSeek 用量侧边栏插件(DeepSeek Harness Web UI)
|
|
|
11
11
|
## 功能
|
|
12
12
|
|
|
13
13
|
- **余额**:`¥ 110.00` 大字显示(符号随账号币种自动切换:人民币 `¥` / 美元 `$`)
|
|
14
|
-
-
|
|
14
|
+
- **档单价**:`→|1.5 ⚡0.05 |→4.5`(输入 / 输入缓存 / 输出,位于卡片右下角;随当前启用模型与峰谷时段自动切换,**切换模型时立即同步**)
|
|
15
15
|
- **峰谷指示灯**:卡片右上角,高峰(9:00–12:00、14:00–18:00 北京时间)橙色,空闲绿色
|
|
16
|
-
- **自动刷新**:每 5
|
|
16
|
+
- **自动刷新**:每 5 分钟一次;点击卡片任意位置立即刷新
|
|
17
17
|
- 跟随应用明暗主题
|
|
18
18
|
|
|
19
19
|
## 安装
|
package/lib/client.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 侧边栏底部用量卡片(注册进 `sidebar.footer.action` 槽位,独占一行,位于
|
|
4
4
|
// Cordis 面板按钮下方)。轮询宿主路由 `/api/deepseek-usage`(见 lib/index.js)
|
|
5
|
-
// 每 5
|
|
6
|
-
//
|
|
5
|
+
// 每 5 分钟一次,点击卡片任意位置可立即刷新。
|
|
6
|
+
// 卡片显示:余额(放大显示,符号随币种 ¥/$)+ 当前模型档单价(输入/输入缓存/
|
|
7
|
+
// 输出,图标 + 数字,位于卡片右下角)+ 峰谷指示灯(右上角)。
|
|
7
8
|
// 样式仅使用 `--dsw-*` 主题变量(含兜底值),跟随明暗主题。
|
|
8
9
|
window.__ModuleLoader__.load({
|
|
9
10
|
id: "@dougen/dsh-deepseek-usage",
|
|
@@ -44,8 +45,8 @@ window.__ModuleLoader__.load({
|
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
const svgBase = {
|
|
47
|
-
width:
|
|
48
|
-
height:
|
|
48
|
+
width: 12,
|
|
49
|
+
height: 12,
|
|
49
50
|
viewBox: "0 0 24 24",
|
|
50
51
|
fill: "none",
|
|
51
52
|
stroke: "currentColor",
|
|
@@ -54,27 +55,29 @@ window.__ModuleLoader__.load({
|
|
|
54
55
|
strokeLinejoin: "round",
|
|
55
56
|
style: { flex: "none", display: "block" },
|
|
56
57
|
};
|
|
58
|
+
// 输入:右箭头指向右边界(→|,数据进入边界)
|
|
57
59
|
const IconIn = () => h("svg", svgBase,
|
|
58
|
-
h("path", { d: "
|
|
59
|
-
h("path", { d: "
|
|
60
|
+
h("path", { d: "M20 4v16" }),
|
|
61
|
+
h("path", { d: "M20 12H6" }),
|
|
62
|
+
h("path", { d: "M10 7l5 5-5 5" }));
|
|
63
|
+
// 输出:左边界发出右箭头(|→,数据从边界流出)
|
|
60
64
|
const IconOut = () => h("svg", svgBase,
|
|
61
|
-
h("path", { d: "
|
|
62
|
-
h("path", { d: "
|
|
65
|
+
h("path", { d: "M4 4v16" }),
|
|
66
|
+
h("path", { d: "M4 12h14" }),
|
|
67
|
+
h("path", { d: "M14 7l5 5-5 5" }));
|
|
68
|
+
// 缓存:闪电(缓存命中,快)
|
|
63
69
|
const IconCache = () => h("svg", svgBase,
|
|
64
|
-
h("
|
|
65
|
-
h("path", { d: "M4 5v14c0 1.7 3.6 3 8 3s8-1.3 8-3V5" }),
|
|
66
|
-
h("path", { d: "M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3" }));
|
|
70
|
+
h("path", { d: "M13 2 3 14h7l-1 8 10-12h-7l1-8z" }));
|
|
67
71
|
|
|
68
72
|
// ---- the widget -------------------------------------------------
|
|
69
73
|
function UsageCell(props) {
|
|
74
|
+
const ctx = props.ctx;
|
|
70
75
|
const wide = !!props.wide;
|
|
71
76
|
const [data, setData] = useState(null);
|
|
72
77
|
const [error, setError] = useState("");
|
|
73
|
-
const [spinning, setSpinning] = useState(false);
|
|
74
78
|
const mounted = useRef(true);
|
|
75
79
|
|
|
76
80
|
const load = useCallback(async () => {
|
|
77
|
-
setSpinning(true);
|
|
78
81
|
try {
|
|
79
82
|
const res = await fetch(USAGE_PATH, { cache: "no-store" });
|
|
80
83
|
let body = null;
|
|
@@ -90,8 +93,6 @@ window.__ModuleLoader__.load({
|
|
|
90
93
|
} catch (e) {
|
|
91
94
|
if (!mounted.current) return;
|
|
92
95
|
setError(e instanceof Error ? e.message : String(e));
|
|
93
|
-
} finally {
|
|
94
|
-
if (mounted.current) setSpinning(false);
|
|
95
96
|
}
|
|
96
97
|
}, []);
|
|
97
98
|
|
|
@@ -99,11 +100,25 @@ window.__ModuleLoader__.load({
|
|
|
99
100
|
mounted.current = true;
|
|
100
101
|
load();
|
|
101
102
|
const timer = setInterval(load, POLL_MS);
|
|
103
|
+
// 模型切换(写入 settings 触发 document-updated)时立即刷新,价格同步。
|
|
104
|
+
let off = null;
|
|
105
|
+
try {
|
|
106
|
+
if (ctx && ctx.remote && typeof ctx.remote.$on === "function") {
|
|
107
|
+
off = ctx.remote.$on("settings/document-updated", () => {
|
|
108
|
+
try { load(); } catch (e) { /* 忽略刷新错误 */ }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
off = null;
|
|
113
|
+
}
|
|
102
114
|
return () => {
|
|
103
115
|
mounted.current = false;
|
|
104
116
|
clearInterval(timer);
|
|
117
|
+
try {
|
|
118
|
+
if (typeof off === "function") off();
|
|
119
|
+
} catch (e) { /* 忽略退订错误 */ }
|
|
105
120
|
};
|
|
106
|
-
}, [load]);
|
|
121
|
+
}, [load, ctx]);
|
|
107
122
|
|
|
108
123
|
const balance = data && data.balance ? data.balance : null;
|
|
109
124
|
const tier = data && data.tier ? data.tier : null;
|
|
@@ -146,10 +161,9 @@ window.__ModuleLoader__.load({
|
|
|
146
161
|
}
|
|
147
162
|
|
|
148
163
|
const Price = (icon, label, value) =>
|
|
149
|
-
h("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, minWidth: 0, color: C.sub } },
|
|
164
|
+
h("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, minWidth: 0, color: C.sub, whiteSpace: "nowrap" } },
|
|
150
165
|
h("span", { style: { display: "inline-flex", alignItems: "center" }, title: label }, icon),
|
|
151
|
-
h("span", { style: { fontWeight: 600
|
|
152
|
-
const Sep = () => h("span", { style: { color: C.sub, margin: "0 3px", fontSize: 10 } }, "·");
|
|
166
|
+
h("span", { style: { fontWeight: 600 } }, fmtPrice(value)));
|
|
153
167
|
|
|
154
168
|
return h("div", {
|
|
155
169
|
style: {
|
|
@@ -174,53 +188,42 @@ window.__ModuleLoader__.load({
|
|
|
174
188
|
title,
|
|
175
189
|
onClick: load,
|
|
176
190
|
},
|
|
177
|
-
// 第一行:余额(24px,符号与数字 3px
|
|
191
|
+
// 第一行:余额(24px,符号与数字 3px 间距)
|
|
178
192
|
h("div", { style: { display: "flex", alignItems: "baseline", minWidth: 0, paddingRight: 14, gap: 6 } },
|
|
179
193
|
h("span", { style: { fontWeight: 700, fontSize: 24, color: C.label, lineHeight: "28px", marginRight: 3 } }, symbol),
|
|
180
|
-
h("span", { style: { fontWeight: 700, fontSize: 24, color: C.label, lineHeight: "28px" } }, money(balance && balance.total)),
|
|
181
|
-
h("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, marginLeft: 10 } },
|
|
182
|
-
Price(h(IconIn, null), "输入", tier && tier.input),
|
|
183
|
-
Sep(),
|
|
184
|
-
Price(h(IconCache, null), "输入缓存", tier && tier.cacheRead),
|
|
185
|
-
Sep(),
|
|
186
|
-
Price(h(IconOut, null), "输出", tier && tier.output))),
|
|
194
|
+
h("span", { style: { fontWeight: 700, fontSize: 24, color: C.label, lineHeight: "28px" } }, money(balance && balance.total))),
|
|
187
195
|
h("span", {
|
|
188
196
|
style: { position: "absolute", top: 8, right: 9, ...dot },
|
|
189
197
|
title: data && data.peak ? "高峰时段 " + (data.peakHours || "") : "空闲时段",
|
|
190
198
|
}),
|
|
191
|
-
//
|
|
199
|
+
// 当前档单价:卡片右下角
|
|
192
200
|
h("span", {
|
|
193
|
-
|
|
194
|
-
style: { position: "absolute", bottom: 8, right: 9, display: "inline-flex", alignItems: "center", color: C.sub },
|
|
195
|
-
title: "刷新中",
|
|
201
|
+
style: { position: "absolute", right: 9, bottom: 4, display: "inline-flex", alignItems: "center", gap: 5 },
|
|
196
202
|
},
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
: null),
|
|
203
|
+
Price(h(IconIn, null), "输入", tier && tier.input),
|
|
204
|
+
Price(h(IconCache, null), "输入缓存", tier && tier.cacheRead),
|
|
205
|
+
Price(h(IconOut, null), "输出", tier && tier.output)),
|
|
201
206
|
errs.length
|
|
202
207
|
? h("div", { style: { color: C.err, fontSize: 10, whiteSpace: "pre-wrap" } }, errs.join(" | "))
|
|
203
208
|
: null);
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
// ---- client plugin body -----------------------------------------
|
|
207
|
-
const inject = ["slots"];
|
|
212
|
+
const inject = ["slots", "remote"];
|
|
208
213
|
|
|
209
214
|
function apply(ctx) {
|
|
210
|
-
// 注入本包样式:footerActions
|
|
215
|
+
// 注入本包样式:footerActions 换行(卡片独占一行)。
|
|
211
216
|
const tagId = "dsh-deepseek-usage";
|
|
212
217
|
if (typeof document !== "undefined" && document.querySelector(`style[data-plugin-css="${tagId}"]`) === null) {
|
|
213
218
|
const style = document.createElement("style");
|
|
214
219
|
style.dataset.plugin = "dsh-deepseek-usage";
|
|
215
220
|
style.dataset.pluginCss = tagId;
|
|
216
|
-
style.textContent =
|
|
217
|
-
'div[class*="footerActions"]{flex-wrap:wrap}' +
|
|
218
|
-
"@keyframes dsh-usage-spin{to{transform:rotate(360deg)}}";
|
|
221
|
+
style.textContent = 'div[class*="footerActions"]{flex-wrap:wrap}';
|
|
219
222
|
document.head.appendChild(style);
|
|
220
223
|
}
|
|
221
224
|
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register(
|
|
222
225
|
{ name: "sidebar.footer.action", id: "dsh-usage", order: 100, label: "DeepSeek 用量" },
|
|
223
|
-
(props) => h(UsageCell, { wide: props.wide }),
|
|
226
|
+
(props) => h(UsageCell, { wide: props.wide, ctx }),
|
|
224
227
|
));
|
|
225
228
|
}
|
|
226
229
|
|
package/lib/index.js
CHANGED
|
@@ -93,6 +93,16 @@ function apply(ctx) {
|
|
|
93
93
|
return sendJson(res, 403, { ok: false, error: "forbidden", message: "仅允许本机访问" });
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// ---- 当前模型当前档单价(展示用)----
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
let model = DEFAULT_MODEL;
|
|
99
|
+
try {
|
|
100
|
+
const adm = ctx.get("agentDefaultModel");
|
|
101
|
+
const selection = adm && typeof adm.currentSelection === "function" ? adm.currentSelection() : void 0;
|
|
102
|
+
if (selection && typeof selection.model === "string") model = selection.model;
|
|
103
|
+
} catch {}
|
|
104
|
+
const unit = unitAt(model, now);
|
|
105
|
+
|
|
96
106
|
const errors = [];
|
|
97
107
|
|
|
98
108
|
// ---- 余额(官方接口,API Key)----
|
|
@@ -124,15 +134,7 @@ function apply(ctx) {
|
|
|
124
134
|
errors.push(`余额获取失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
// ----
|
|
128
|
-
const now = Date.now();
|
|
129
|
-
let model = DEFAULT_MODEL;
|
|
130
|
-
try {
|
|
131
|
-
const adm = ctx.get("agentDefaultModel");
|
|
132
|
-
const selection = adm && typeof adm.currentSelection === "function" ? adm.currentSelection() : void 0;
|
|
133
|
-
if (selection && typeof selection.model === "string") model = selection.model;
|
|
134
|
-
} catch {}
|
|
135
|
-
const unit = unitAt(model, now);
|
|
137
|
+
// ---- 档单价提示(未知模型回退 chat 档)----
|
|
136
138
|
if (!unit.known) errors.push(`当前模型 ${model} 不在价格表,档单价按 deepseek-chat 展示`);
|
|
137
139
|
|
|
138
140
|
sendJson(res, 200, {
|
package/package.json
CHANGED
package/screenshot.png
CHANGED
|
Binary file
|