dsh-deepseek-balance-widget 2.2.0 → 2.3.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/lib/client.js CHANGED
@@ -110,7 +110,9 @@ window.__ModuleLoader__.load({
110
110
  pasteCookieHint: "请粘贴 Cookie 或 cURL,AI 会自动提取",
111
111
  copied: "已复制",
112
112
  copyFailed: "复制失败",
113
- refresh: "刷新"
113
+ refresh: "刷新",
114
+ peakPeriod: "繁忙时段",
115
+ offPeakPeriod: "空闲时段"
114
116
  },
115
117
  en: {
116
118
  entryAriaLabel: "AI Balance (Balance / Today Spend / Today Tokens)",
@@ -193,7 +195,9 @@ window.__ModuleLoader__.load({
193
195
  pasteCookieHint: "Paste a Cookie or cURL; the AI will extract it",
194
196
  copied: "Copied",
195
197
  copyFailed: "Copy failed",
196
- refresh: "Refresh"
198
+ refresh: "Refresh",
199
+ peakPeriod: "Peak",
200
+ offPeakPeriod: "Off-peak"
197
201
  }
198
202
  };
199
203
  /** Authoritative source: host route reads ~/.dsh/settings.yaml locale.preference. */
@@ -385,6 +389,9 @@ window.__ModuleLoader__.load({
385
389
  ".dshBalanceTabBal{font-size:12px;font-weight:600;color:#3fb950;font-variant-numeric:tabular-nums}",
386
390
  ".dshBalanceTabBal[data-state=err]{color:#f85149}",
387
391
  ".dshBalanceTabTitle{font-size:14px;font-weight:600}",
392
+ ".dshBalancePeriodBadge{font-size:11px;padding:2px 6px;border-radius:4px;color:#9aa4b2;background:rgba(255,255,255,.08);white-space:nowrap}",
393
+ ".dshBalancePeriodBadge[data-period=peak]{color:#f85149;background:rgba(248,81,73,.12)}",
394
+ ".dshBalancePeriodBadge[data-period=offPeak]{color:#3fb950;background:rgba(35,197,94,.12)}",
388
395
  ".dshBalanceTabMenu{appearance:none;cursor:pointer;margin-left:4px;font-size:16px;font-weight:600;line-height:1;color:#7b8494;user-select:none;width:18px;height:18px;display:flex;align-items:center;justify-content:center;border-radius:4px}",
389
396
  ".dshBalanceTabMenu:hover{color:#f85149;background:rgba(248,81,73,.12)}",
390
397
  ".dshBalanceCtxMenu{position:absolute;z-index:10000;background:#161b22;border:1px solid rgba(255,255,255,.12);border-radius:6px;box-shadow:0 8px 24px rgba(0,0,0,.35);padding:4px 0;min-width:120px}",
@@ -1058,6 +1065,12 @@ window.__ModuleLoader__.load({
1058
1065
  return html;
1059
1066
  }
1060
1067
 
1068
+ /** DeepSeek peak/off-peak period based on Beijing time (UTC+8). Peak: 09-12, 14-18. */
1069
+ function currentDeepseekPeriod() {
1070
+ const hour = (new Date().getUTCHours() + 8) % 24;
1071
+ return ((hour >= 9 && hour < 12) || (hour >= 14 && hour < 18)) ? "peak" : "offPeak";
1072
+ }
1073
+
1061
1074
  /** Build one tab button for the provider switcher. */
1062
1075
  function providerTabHtml(p, bal, isActive) {
1063
1076
  const meta = PROVIDER_META[p.kind] || { label: p.kind };
@@ -1072,8 +1085,12 @@ window.__ModuleLoader__.load({
1072
1085
  '<span class="dshBalanceTabMenu" data-tab-remove type="button" role="button" aria-label="' + t("remove") + '" title="' + t("remove") + '">×</span>';
1073
1086
  const titleCls = isActive ? ' class="dshBalanceTabTitle"' : '';
1074
1087
  const balanceSpan = isActive ? '' : '<span class="dshBalanceTabBal"' + stateAttr + ">" + balanceText + "</span>";
1088
+ const period = currentDeepseekPeriod();
1089
+ const periodBadge = (isActive && p.kind === "deepseek")
1090
+ ? '<span class="dshBalancePeriodBadge" data-period="' + period + '">' + t(period + "Period") + "</span>"
1091
+ : "";
1075
1092
  return '<button class="dshBalanceTab' + (isActive ? " active" : "") + '" type="button" data-tab="' + escapeHtml(p.id) + '">' +
1076
- '<span' + titleCls + '>' + escapeHtml(label) + '</span>' + balanceSpan + removeBtn + "</button>";
1093
+ '<span' + titleCls + '>' + escapeHtml(label) + '</span>' + periodBadge + balanceSpan + removeBtn + "</button>";
1077
1094
  }
1078
1095
 
1079
1096
  /** Build the detail pane for the currently active provider. */
package/lib/index.js CHANGED
@@ -12,7 +12,7 @@ const name = "deepseek-balance-widget";
12
12
  const inject = ["webServer", "credentials"];
13
13
 
14
14
  /** NPM registry endpoint for this package (public access, no auth). */
15
- const NPM_REGISTRY_URL = "https://registry.npmjs.org/dsh-deepseek-balance-widget/latest";
15
+ const NPM_REGISTRY_URL = "https://registry.npmjs.org/dsh-deepseek-balance-widget";
16
16
 
17
17
  /** Absolute path to this plugin's package root (where package.json lives). */
18
18
  const PLUGIN_ROOT = (() => {
@@ -480,7 +480,25 @@ const PLATFORM_HEADERS = {
480
480
  };
481
481
 
482
482
  /**
483
- * Query the NPM registry for the latest published version of this package.
483
+ * Simple semver comparison for x.y.z style versions.
484
+ * @returns {number} 1 if a > b, -1 if a < b, 0 if equal.
485
+ */
486
+ function compareSemver(a, b) {
487
+ const toParts = (v) => String(v).split(".").map((p) => Number.parseInt(p, 10) || 0);
488
+ const ap = toParts(a);
489
+ const bp = toParts(b);
490
+ for (let i = 0; i < Math.max(ap.length, bp.length); i++) {
491
+ const av = ap[i] ?? 0;
492
+ const bv = bp[i] ?? 0;
493
+ if (av > bv) return 1;
494
+ if (av < bv) return -1;
495
+ }
496
+ return 0;
497
+ }
498
+
499
+ /**
500
+ * Query the NPM registry for the highest published version of this package.
501
+ * The popover "update" button will install this version regardless of the "latest" dist-tag.
484
502
  * @returns {Promise<{ok:boolean, local:string, latest?:string, updateAvailable?:boolean, error?:string}>}
485
503
  */
486
504
  async function queryNpmVersion() {
@@ -500,21 +518,15 @@ async function queryNpmVersion() {
500
518
  const text = await response.text();
501
519
  return { ok: false, local: LOCAL_VERSION, error: `bad json (HTTP ${response.status})` };
502
520
  }
503
- const latest = data?.version;
504
- if (typeof latest !== "string") {
505
- return { ok: false, local: LOCAL_VERSION, error: "version field missing" };
521
+ const versions = data && typeof data.versions === "object" ? Object.keys(data.versions) : [];
522
+ if (!versions.length) {
523
+ return { ok: false, local: LOCAL_VERSION, error: "version list missing" };
506
524
  }
507
- // Simple semantic-ish comparison: split by dot and compare numeric parts.
508
- const toParts = (v) => String(v).split(".").map((p) => Number.parseInt(p, 10) || 0);
509
- const localParts = toParts(LOCAL_VERSION);
510
- const latestParts = toParts(latest);
511
- let updateAvailable = false;
512
- for (let i = 0; i < Math.max(localParts.length, latestParts.length); i++) {
513
- const a = localParts[i] ?? 0;
514
- const b = latestParts[i] ?? 0;
515
- if (b > a) { updateAvailable = true; break; }
516
- if (b < a) { break; }
525
+ const latest = versions.reduce((max, v) => (compareSemver(v, max) > 0 ? v : max), versions[0]);
526
+ if (typeof latest !== "string") {
527
+ return { ok: false, local: LOCAL_VERSION, error: "could not determine highest version" };
517
528
  }
529
+ const updateAvailable = compareSemver(latest, LOCAL_VERSION) > 0;
518
530
  return { ok: true, local: LOCAL_VERSION, latest, updateAvailable };
519
531
  }
520
532
 
@@ -545,12 +557,20 @@ async function detectPackageManager() {
545
557
  * @returns {Promise<{ok:boolean, output:string, error?:string}>}
546
558
  */
547
559
  async function runPackageUpdate() {
560
+ const versionInfo = await queryNpmVersion();
561
+ if (!versionInfo.ok) {
562
+ return { ok: false, error: versionInfo.error || "failed to query npm version" };
563
+ }
564
+ if (!versionInfo.updateAvailable) {
565
+ return { ok: true, output: "already up to date", noOp: true };
566
+ }
567
+ const target = versionInfo.latest;
548
568
  const pm = await detectPackageManager();
549
569
  const isWin = process.platform === "win32";
550
570
  const cmd = isWin ? (pm === "pnpm" ? "pnpm.cmd" : "npm.cmd") : pm;
551
571
  const args = pm === "pnpm"
552
- ? ["add", "dsh-deepseek-balance-widget@latest"]
553
- : ["install", "dsh-deepseek-balance-widget@latest"];
572
+ ? ["add", `dsh-deepseek-balance-widget@${target}`]
573
+ : ["install", `dsh-deepseek-balance-widget@${target}`];
554
574
  return new Promise((resolve) => {
555
575
  const child = spawn(cmd, args, {
556
576
  cwd: UPDATE_CWD,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-deepseek-balance-widget",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "description": "Multi-provider AI balance widget for the dsh web sidebar: a live, auto-refreshing balance pill plus a detail popover listing DeepSeek and any added providers (MiMo etc.). Keys are stored per-machine in ~/.dsh/ai-balances.json and resolved from the local credential seam, never hardcoded.",
6
6
  "keywords": [