@dougen/dsh-deepseek-usage 0.6.0 → 0.6.1
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 +6 -6
- package/lib/client.js +73 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ DeepSeek **余额侧边栏插件**(DeepSeek Harness Web UI):在 DSH **网
|
|
|
12
12
|
- **Unit pricing**: `→|1.5 ⚡0.05 |→4.5` (input / input cache / output, bottom-right of the card; switches with the active model and peak/off-peak window — **updates instantly when you switch models**)
|
|
13
13
|
- **Auto price sync**: unit prices are re-fetched from the official DeepSeek pricing page on first use each day and when you click the card — DeepSeek price changes no longer require a plugin update. The page is parsed **structurally (HTML table cells)**, not by matching surrounding text, so official layout/rewording changes no longer break it; a plain-text strategy remains as a fallback
|
|
14
14
|
- **Rename-proof model mapping**: when DeepSeek renames a model (e.g. `deepseek-v4-flash` → `deepseek-flash`) the old id is charged at the current model's price, and the card notes `Priced as deepseek-flash` instead of silently showing an outdated built-in price
|
|
15
|
-
- **Visible sync state**: if the official page cannot be fetched/parsed, the card says prices are not synced (hover for the error) instead of quietly showing stale built-in numbers
|
|
15
|
+
- **Visible sync state**: if the official page cannot be fetched/parsed, the card is blanked and says prices are not synced (hover for the error) instead of quietly showing stale built-in numbers
|
|
16
16
|
- **Peak indicator**: top-right dot, orange during peak hours (Mon–Fri 9:00–12:00, 14:00–18:00 Beijing Time), green otherwise
|
|
17
17
|
- **Auto refresh**: every 5 minutes; click the card to refresh instantly
|
|
18
18
|
- **i18n**: UI copy follows the DSH locale (Chinese / English)
|
|
@@ -22,7 +22,7 @@ DeepSeek **余额侧边栏插件**(DeepSeek Harness Web UI):在 DSH **网
|
|
|
22
22
|
- **档单价**:`→|1.5 ⚡0.05 |→4.5`(输入 / 输入缓存 / 输出,位于卡片右下角;随当前启用模型与峰谷时段自动切换,**切换模型时立即同步**)
|
|
23
23
|
- **单价自动同步**:每日首次使用或点击卡片时,自动从官方价格页拉取最新档单价;官方调价无需更新插件。页面解析采用**结构化表格解析**(直接读 HTML 表格单元格),不再依赖"正文里某段文字长什么样",官方改版/改文案不会失效;另保留纯文本兜底策略
|
|
24
24
|
- **抗改名映射**:官方给模型改名(如 `deepseek-v4-flash` → `deepseek-flash`)时,旧名按现名价格计费,卡片会注明 `按 deepseek-flash 计价`,不会静默显示内置旧价
|
|
25
|
-
-
|
|
25
|
+
- **同步状态可见**:官方页抓取/解析失败时,卡片会置空并提示"价格未同步"(悬停可看具体错误),而不是悄悄显示内置旧价
|
|
26
26
|
- **峰谷指示灯**:卡片右上角,高峰(周一至周五 9:00–12:00、14:00–18:00 北京时间)橙色,空闲绿色
|
|
27
27
|
- **自动刷新**:每 5 分钟一次;点击卡片任意位置立即刷新
|
|
28
28
|
- **多语言**:界面文案跟随 DSH 语言(中文 / English)
|
|
@@ -69,21 +69,21 @@ Make sure `DEEPSEEK_API_KEY` is configured in DSH (Model settings page) before u
|
|
|
69
69
|
**Balance shows "—"? / 余额显示"—"?**
|
|
70
70
|
|
|
71
71
|
- `DEEPSEEK_API_KEY` is not configured — configure it in DSH settings and restart.
|
|
72
|
-
- Network or API error — the
|
|
72
|
+
- Network or API error — the card is blanked and shows only the error text; click it to retry.
|
|
73
73
|
|
|
74
74
|
- 未配置 `DEEPSEEK_API_KEY`:在 DSH 设置中配置后重启。
|
|
75
|
-
-
|
|
75
|
+
- 网络或接口异常:卡片会置空并只显示错误文字,点击卡片可重试。
|
|
76
76
|
|
|
77
77
|
**Price differs from the official one? / 单价与官方价格不一致?**
|
|
78
78
|
|
|
79
79
|
- The unit price switches with the **active model** and the **peak/off-peak window**; prices are re-synced from the official pricing page daily (first use) or on card click. If the active model is not on the official page nor in the built-in table, `deepseek-chat` pricing is shown with a notice.
|
|
80
|
-
-
|
|
80
|
+
- Any such notice (or an error) blanks the card: balance, unit prices and the peak dot are hidden and only the notice text is shown (hover it for the exact error). Purely informational notices (priced as a renamed or built-in model) are dismissable: **click the card to flip back to the normal balance/price card** — the notice stays reachable from the peak dot's tooltip, and clicking again brings it back. Sync failures and API errors always keep the card blanked; there, clicking only refreshes. The text tells you where the number comes from:
|
|
81
81
|
- *Prices not synced (built-in table)* — the official page could not be fetched/parsed; click the card to retry (hover the text for the exact error).
|
|
82
82
|
- *Priced as `deepseek-flash`* — the active model id was renamed by DeepSeek; the current model's price is used.
|
|
83
83
|
- *Priced as built-in `…`* — the model is not listed on the official page; a built-in entry is used.
|
|
84
84
|
|
|
85
85
|
- 单价按**当前启用模型**与**峰谷时段**自动切换;单价每日(首次使用)或点击卡片时从官方价格页同步。若当前模型不在官方价格页也不在内置表,会按 `deepseek-chat` 档展示并给出提示。
|
|
86
|
-
-
|
|
86
|
+
- 一旦出现这类提示或错误,卡片会**置空**:不显示余额、档单价与峰谷灯,只显示提示文字(悬停看具体错误)。其中纯说明性的良性提示(按现名计价 / 按内置档计价)可以**点击卡片切回普通状态**(余额 + 档单价 + 峰谷灯),提示此时保留在峰谷灯的 hover 里,再点一下切回提示;同步失败与接口错误属于异常,卡片始终置空(此时点击只刷新)。文字说明数字来自哪里:
|
|
87
87
|
- *价格未同步(用内置表)*:官方页抓取/解析失败,点击卡片可重试(悬停看具体错误)。
|
|
88
88
|
- *按 `deepseek-flash` 计价*:官方给该模型改名了,已按现名价格计费。
|
|
89
89
|
- *按内置 `…` 档计价*:该模型不在官方价格页,用了内置表。
|
package/lib/client.js
CHANGED
|
@@ -19,6 +19,9 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
// ---- constants -------------------------------------------------
|
|
20
20
|
const POLL_MS = 5 * 60 * 1000;
|
|
21
21
|
const USAGE_PATH = "/api/deepseek-usage";
|
|
22
|
+
// 常态卡片高度:上下 padding 8+8 + 余额行 lineHeight 28 + 边框 2 = 46。
|
|
23
|
+
// 异常态把卡片置空时用它兜住高度,侧边栏高度不跳动。
|
|
24
|
+
const CARD_MIN_HEIGHT = 46;
|
|
22
25
|
|
|
23
26
|
// ---- i18n(DSH locale 服务;zh 为兜底)----
|
|
24
27
|
const NS = "dsh-deepseek-usage";
|
|
@@ -100,6 +103,9 @@ window.__ModuleLoader__.load({
|
|
|
100
103
|
const wide = !!props.wide;
|
|
101
104
|
const [data, setData] = useState(null);
|
|
102
105
|
const [error, setError] = useState("");
|
|
106
|
+
// 纯良性提示(模型改名 / 官方页没列该模型)时卡片默认显示提示,点击可切回普通
|
|
107
|
+
// 状态(余额 + 单价 + 峰谷灯),再点切回提示。异常不受它影响,始终置空。
|
|
108
|
+
const [showNormal, setShowNormal] = useState(false);
|
|
103
109
|
const mounted = useRef(true);
|
|
104
110
|
const locale = ctx && ctx.locale;
|
|
105
111
|
|
|
@@ -171,20 +177,27 @@ window.__ModuleLoader__.load({
|
|
|
171
177
|
const errs = (Array.isArray(data && data.errors) ? data.errors : []).concat(error ? [error] : []);
|
|
172
178
|
// 价格来源提示:模型改名按现名计价 / 价格取自内置表(同步失败或该模型不在官方页)
|
|
173
179
|
// 时明示,避免"官方已调价但卡片静静显示内置旧价"。
|
|
180
|
+
// friendly=true 表示只是"价格从哪来"的良性说明(可点击切回普通状态);
|
|
181
|
+
// friendly=false 表示同步失败/旧价兜底,属于异常,必须一直可见。
|
|
174
182
|
const hints = [];
|
|
183
|
+
const addHint = (text, friendly) => hints.push({ text, friendly });
|
|
175
184
|
if (pricing && typeof pricing === "object") {
|
|
176
185
|
const used = pricing.used ? String(pricing.used) : "";
|
|
177
186
|
if (pricing.via === "remote-alias" || pricing.via === "remote-family") {
|
|
178
|
-
|
|
187
|
+
addHint(t("price.alias").replace("{model}", used), true);
|
|
179
188
|
} else if (pricing.source === "seed" && pricing.via !== "fallback") {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
: t("price.builtin").replace("{model}", used));
|
|
189
|
+
if (pricing.syncOk === false) addHint(t("price.stale"), false);
|
|
190
|
+
else addHint(t("price.builtin").replace("{model}", used), true);
|
|
183
191
|
} else if (pricing.syncOk === false && pricing.syncedAt) {
|
|
184
192
|
// 本次刷新失败,但仍在用上次成功同步到的官方价格
|
|
185
|
-
|
|
193
|
+
addHint(t("price.lastGood"), false);
|
|
186
194
|
}
|
|
187
195
|
}
|
|
196
|
+
const hintText = hints.map((x) => x.text).join(" · ");
|
|
197
|
+
// blocking:同步失败/接口错误这类异常,卡片始终置空、点击只刷新;
|
|
198
|
+
// blanked:异常,或"纯良性提示且用户还没点开普通状态"。
|
|
199
|
+
const blocking = errs.length > 0 || hints.some((x) => !x.friendly);
|
|
200
|
+
const blanked = blocking || (hints.length > 0 && !showNormal);
|
|
188
201
|
const hintTitle = pricing && typeof pricing.syncError === "string" ? pricing.syncError : "";
|
|
189
202
|
const dot = {
|
|
190
203
|
width: 8,
|
|
@@ -195,9 +208,14 @@ window.__ModuleLoader__.load({
|
|
|
195
208
|
boxShadow: "0 0 4px rgba(0,0,0,.3)",
|
|
196
209
|
};
|
|
197
210
|
// 指示灯 hover 提示只显示当前处于高峰/空闲时段,不拼时段明细(太长);
|
|
198
|
-
//
|
|
211
|
+
// 价格来源提示附在末尾——普通状态(良性提示被点开后)它是提示唯一可见处。
|
|
199
212
|
const peakTitle = (data && data.peak ? t("peak.peak") : t("peak.off"))
|
|
200
|
-
+ (hints.length ? " · " +
|
|
213
|
+
+ (hints.length ? " · " + hintText : "");
|
|
214
|
+
// 点击卡片:刷新数据;若是纯良性提示,再在「提示态 / 普通态」之间切换。
|
|
215
|
+
const onCardClick = () => {
|
|
216
|
+
if (hints.length > 0 && !blocking) setShowNormal((v) => !v);
|
|
217
|
+
load(true);
|
|
218
|
+
};
|
|
201
219
|
|
|
202
220
|
if (!wide) {
|
|
203
221
|
return h("div", {
|
|
@@ -224,6 +242,48 @@ window.__ModuleLoader__.load({
|
|
|
224
242
|
h("span", { style: dot, title: peakTitle }));
|
|
225
243
|
}
|
|
226
244
|
|
|
245
|
+
// A2/A3(价格来源提示 / 错误)出现时,卡片直接置空:不画余额、档单价与峰谷灯,
|
|
246
|
+
// 只在空白卡片上显示提示文字——提示不再和余额数字、单价行抢同一块版面,从根本
|
|
247
|
+
// 上消除遮挡。minHeight 兜住常态卡片高度(CARD_MIN_HEIGHT),侧边栏不跳动;
|
|
248
|
+
// 文字多行时卡片自然变高。纯良性提示可由点击切回普通状态(见 blanked)。
|
|
249
|
+
// 窄卡片(!wide)没有文字空间,维持原样(提示在峰谷灯 hover 里)。
|
|
250
|
+
if (blanked) {
|
|
251
|
+
return h("div", {
|
|
252
|
+
style: {
|
|
253
|
+
boxSizing: "border-box",
|
|
254
|
+
flex: "1 1 100%",
|
|
255
|
+
minWidth: 0,
|
|
256
|
+
minHeight: CARD_MIN_HEIGHT,
|
|
257
|
+
padding: "8px 10px",
|
|
258
|
+
borderRadius: 10,
|
|
259
|
+
border: "1px solid " + C.border,
|
|
260
|
+
background: C.bg,
|
|
261
|
+
color: C.label,
|
|
262
|
+
cursor: "pointer",
|
|
263
|
+
userSelect: "none",
|
|
264
|
+
display: "flex",
|
|
265
|
+
flexDirection: "column",
|
|
266
|
+
justifyContent: "center",
|
|
267
|
+
gap: 3,
|
|
268
|
+
marginTop: 4,
|
|
269
|
+
fontSize: "12px",
|
|
270
|
+
lineHeight: "15px",
|
|
271
|
+
},
|
|
272
|
+
onClick: onCardClick,
|
|
273
|
+
},
|
|
274
|
+
hints.length
|
|
275
|
+
? h("div", {
|
|
276
|
+
style: { color: C.sub, fontSize: 10, lineHeight: "13px", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" },
|
|
277
|
+
title: hintTitle || hintText,
|
|
278
|
+
}, hintText)
|
|
279
|
+
: null,
|
|
280
|
+
errs.length
|
|
281
|
+
? h("div", {
|
|
282
|
+
style: { color: C.err, fontSize: 10, lineHeight: "13px", whiteSpace: "pre-wrap", overflowWrap: "anywhere" },
|
|
283
|
+
}, errs.join(" | "))
|
|
284
|
+
: null);
|
|
285
|
+
}
|
|
286
|
+
|
|
227
287
|
const Price = (icon, label, value) =>
|
|
228
288
|
h("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, minWidth: 0, color: C.sub, whiteSpace: "nowrap" } },
|
|
229
289
|
h("span", { style: { display: "inline-flex", alignItems: "center" }, title: label }, icon),
|
|
@@ -249,7 +309,7 @@ window.__ModuleLoader__.load({
|
|
|
249
309
|
fontSize: "12px",
|
|
250
310
|
lineHeight: "15px",
|
|
251
311
|
},
|
|
252
|
-
onClick:
|
|
312
|
+
onClick: onCardClick,
|
|
253
313
|
},
|
|
254
314
|
// 第一行:余额(24px,符号与数字 3px 间距)
|
|
255
315
|
h("div", { style: { display: "flex", alignItems: "baseline", minWidth: 0, paddingRight: 14, gap: 6 } },
|
|
@@ -259,25 +319,17 @@ window.__ModuleLoader__.load({
|
|
|
259
319
|
style: { position: "absolute", top: 8, right: 9, ...dot },
|
|
260
320
|
title: peakTitle,
|
|
261
321
|
}),
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
322
|
+
// 底部行:只剩当前档单价,恒靠右下角。
|
|
323
|
+
// 用 flex-end 而不是 space-between:容器只有一个子元素时 space-between 会把它
|
|
324
|
+
// 推到最左侧(0.6.0 的回归点)。提示文字不在这张卡片上重复显示:异常态由空白
|
|
325
|
+
// 卡片承担,普通态只剩峰谷灯 hover(peakTitle)。
|
|
265
326
|
h("span", {
|
|
266
327
|
style: { position: "absolute", left: 9, right: 9, bottom: 4, display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 6 },
|
|
267
328
|
},
|
|
268
|
-
hints.length
|
|
269
|
-
? h("span", {
|
|
270
|
-
style: { color: C.sub, fontSize: 10, minWidth: 0, marginRight: "auto", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
271
|
-
title: hintTitle || hints.join(" · "),
|
|
272
|
-
}, hints.join(" · "))
|
|
273
|
-
: null,
|
|
274
329
|
h("span", { style: { display: "inline-flex", alignItems: "center", gap: 5, flex: "none" } },
|
|
275
330
|
Price(h(IconIn, null), t("price.input"), tier && tier.input),
|
|
276
331
|
Price(h(IconCache, null), t("price.cache"), tier && tier.cacheRead),
|
|
277
|
-
Price(h(IconOut, null), t("price.output"), tier && tier.output)))
|
|
278
|
-
errs.length
|
|
279
|
-
? h("div", { style: { color: C.err, fontSize: 10, whiteSpace: "pre-wrap" } }, errs.join(" | "))
|
|
280
|
-
: null);
|
|
332
|
+
Price(h(IconOut, null), t("price.output"), tier && tier.output))));
|
|
281
333
|
}
|
|
282
334
|
|
|
283
335
|
// ---- client plugin body -----------------------------------------
|