@dsh-plugins/dsh-llm-hub 1.3.2 → 1.3.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [1.3.3] - 2026-09-25
6
+
7
+ ### ✨ 订阅制配额可视化进度条与重置倒计时
8
+
9
+ - **📊 订阅制可视化进度条胶囊(Subscription Quota Gauge)**:
10
+ - 参考 `claude-usage-statusline` 经典轨道式设计,在 Provider 卡片中引入高质感平滑迷你进度条;
11
+ - 动态三色渐变指示:`> 50%` 充裕翡翠绿(`#10b981`)、`15%~50%` 预警金黄(`#f59e0b`)、`<= 15%` 告急珊瑚红(`#ef4444`);
12
+ - 同时支持主限额(如 MiniMax 5 小时滚动配额、智谱 5h 配额)与次级周限额的双重进度条并列展示;
13
+ - **⏱️ 重置时刻与倒计时精准指示(Reset Time Tracking)**:
14
+ - 在配额进度条后实时标注重置时刻(如 `(15:40 重置)`),悬浮卡片提供完整余量与周期说明;
15
+ - 适配器底层自动解析服务端返回的 `current_interval_end_time` / `next_reset_time` / `expire_time` 真实重置时刻。
16
+
5
17
  ## [1.3.2] - 2026-09-24
6
18
 
7
19
  ### 🐛 修复与健壮性提升
package/lib/client.js CHANGED
@@ -547,6 +547,18 @@ window.__ModuleLoader__.load({
547
547
  '.dsh-llm-hub-latency-badge.is-fast{background:rgba(16,185,129,.08);color:var(--dsw-alias-state-success-primary,#10b981);border:.5px solid rgba(16,185,129,.2)}',
548
548
  '.dsh-llm-hub-latency-badge.is-normal{background:rgba(234,179,8,.08);color:var(--dsw-alias-state-warn-primary,#eab308);border:.5px solid rgba(234,179,8,.2)}',
549
549
  '.dsh-llm-hub-latency-badge.is-slow{background:rgba(239,68,68,.08);color:var(--dsw-alias-state-error-primary,#ef4444);border:.5px solid rgba(239,68,68,.2)}',
550
+ // 订阅制配额可视化进度条与重置倒计时(参考 claude-usage-statusline 规范)
551
+ '.dsh-llm-hub-quota-group{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;vertical-align:middle}',
552
+ '.dsh-llm-hub-quota-bar{display:inline-flex;align-items:center;gap:4.5px;padding:1.5px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,.03));border:.5px solid var(--dsw-alias-border-l1,rgba(255,255,255,.08));font-size:10.5px;line-height:1;transition:all .15s ease}',
553
+ '.dsh-llm-hub-quota-bar:hover{border-color:var(--dsw-alias-brand-primary,rgba(59,130,246,.4));background:var(--dsw-alias-bg-layer-3,rgba(255,255,255,.06))}',
554
+ '.dsh-llm-hub-quota-label{font-weight:600;color:var(--dsw-alias-label-primary);font-size:10px}',
555
+ '.dsh-llm-hub-quota-track{width:46px;height:4.5px;border-radius:2.5px;background:rgba(255,255,255,.12);overflow:hidden;position:relative;display:inline-block;vertical-align:middle}',
556
+ '.dsh-llm-hub-quota-fill{height:100%;border-radius:2.5px;display:block;transition:width .3s cubic-bezier(0.4,0,0.2,1)}',
557
+ '.dsh-llm-hub-quota-fill.is-high{background:linear-gradient(90deg,#10b981,#34d399)}',
558
+ '.dsh-llm-hub-quota-fill.is-mid{background:linear-gradient(90deg,#f59e0b,#fbbf24)}',
559
+ '.dsh-llm-hub-quota-fill.is-low{background:linear-gradient(90deg,#ef4444,#f87171)}',
560
+ '.dsh-llm-hub-quota-pct{font-variant-numeric:tabular-nums;font-weight:600;color:var(--dsw-alias-label-primary);font-size:10.5px}',
561
+ '.dsh-llm-hub-quota-time{font-size:9.5px;color:var(--dsw-alias-label-secondary);opacity:.8;font-variant-numeric:tabular-nums;display:inline-flex;align-items:center;margin-left:1px}',
550
562
  // Webkubor 插件全家桶互导矩阵
551
563
  '.dsh-suite-dock{margin-top:14px;padding:14px 16px;background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,0.03));border:1px solid var(--dsw-alias-border-l1,rgba(255,255,255,0.08));border-radius:10px;backdrop-filter:blur(10px)}',
552
564
  '.dsh-suite-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;flex-wrap:wrap;gap:8px}',
@@ -861,14 +873,44 @@ window.__ModuleLoader__.load({
861
873
  return `${t('balance')} ${symbol}${first.amount}${voucher}`
862
874
  }
863
875
  const meaning = balance.meaning === 'used' ? t('used') : t('remain')
864
- const parts = []
876
+ const isUsed = balance.meaning === 'used'
877
+ const bars = []
865
878
  for (const item of items.slice(0, 2)) {
866
- let piece = `${item.label} ${meaning} ${item.percent}%`
867
- if (typeof item.weeklyPercent === 'number') piece += ` · ${t('week')} ${meaning} ${item.weeklyPercent}%`
868
- parts.push(piece)
879
+ const remainPct = isUsed ? Math.max(0, 100 - (item.percent ?? 0)) : (item.percent ?? 0)
880
+ const fillPct = Math.min(Math.max(remainPct, 0), 100)
881
+ const levelClass = fillPct > 50 ? 'is-high' : (fillPct > 20 ? 'is-mid' : 'is-low')
882
+ const labelText = item.label || '5h'
883
+ const resetTimeNote = item.resetsAt ? `(${item.resetsAt} 重置)` : ''
884
+ bars.push(h('span', {
885
+ key: labelText,
886
+ className: 'dsh-llm-hub-quota-bar',
887
+ title: `${labelText} ${meaning} ${item.percent}%${resetTimeNote ? ` · ${resetTimeNote}` : ''}`
888
+ },
889
+ h('span', { className: 'dsh-llm-hub-quota-label' }, labelText),
890
+ h('span', { className: 'dsh-llm-hub-quota-track' },
891
+ h('span', { className: `dsh-llm-hub-quota-fill ${levelClass}`, style: { width: `${fillPct}%` } })
892
+ ),
893
+ h('span', { className: 'dsh-llm-hub-quota-pct' }, `${item.percent}%`),
894
+ resetTimeNote ? h('span', { className: 'dsh-llm-hub-quota-time' }, resetTimeNote) : null
895
+ ))
896
+ if (typeof item.weeklyPercent === 'number') {
897
+ const weeklyRemain = isUsed ? Math.max(0, 100 - item.weeklyPercent) : item.weeklyPercent
898
+ const weeklyFill = Math.min(Math.max(weeklyRemain, 0), 100)
899
+ const weeklyClass = weeklyFill > 50 ? 'is-high' : (weeklyFill > 20 ? 'is-mid' : 'is-low')
900
+ bars.push(h('span', {
901
+ key: 'week',
902
+ className: 'dsh-llm-hub-quota-bar',
903
+ title: `${t('week')} ${meaning} ${item.weeklyPercent}%`
904
+ },
905
+ h('span', { className: 'dsh-llm-hub-quota-label' }, t('week')),
906
+ h('span', { className: 'dsh-llm-hub-quota-track' },
907
+ h('span', { className: `dsh-llm-hub-quota-fill ${weeklyClass}`, style: { width: `${weeklyFill}%` } })
908
+ ),
909
+ h('span', { className: 'dsh-llm-hub-quota-pct' }, `${item.weeklyPercent}%`)
910
+ ))
911
+ }
869
912
  }
870
- const level = typeof balance.level === 'string' && balance.level.length > 0 ? `${balance.level} · ` : ''
871
- return `${t('planQuota')} ${level}${parts.join(' / ')}`
913
+ return h('span', { className: 'dsh-llm-hub-quota-group' }, bars)
872
914
  }
873
915
 
874
916
  /**
package/lib/index.js CHANGED
@@ -923,10 +923,21 @@ export function apply(ctx) {
923
923
  if (row === null || typeof row !== 'object') continue
924
924
  const percent = percentage(row.current_interval_remaining_percent)
925
925
  if (percent === undefined) continue
926
+ let resetsAt = undefined
927
+ const rawTime = row.current_interval_end_time ?? row.end_time ?? row.reset_time
928
+ if (typeof rawTime === 'number' && rawTime > 0) {
929
+ const date = new Date(rawTime > 1e11 ? rawTime : rawTime * 1000)
930
+ if (!isNaN(date.getTime())) {
931
+ resetsAt = `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
932
+ }
933
+ } else if (typeof rawTime === 'string' && rawTime.trim() !== '') {
934
+ resetsAt = rawTime.trim()
935
+ }
926
936
  items.push({
927
937
  label: label(row.model_name) ?? 'plan',
928
938
  percent,
929
- weeklyPercent: percentage(row.current_weekly_remaining_percent)
939
+ weeklyPercent: percentage(row.current_weekly_remaining_percent),
940
+ resetsAt
930
941
  })
931
942
  }
932
943
  return { items }
@@ -953,7 +964,17 @@ export function apply(ctx) {
953
964
  if (tier === undefined) continue
954
965
  const percent = percentage(lim.percentage)
955
966
  if (percent === undefined) continue
956
- items.push({ label: tier, percent })
967
+ let resetsAt = undefined
968
+ const rawTime = lim.reset_time ?? lim.next_reset_time ?? body?.data?.expire_time
969
+ if (typeof rawTime === 'number' && rawTime > 0) {
970
+ const date = new Date(rawTime > 1e11 ? rawTime : rawTime * 1000)
971
+ if (!isNaN(date.getTime())) {
972
+ resetsAt = `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
973
+ }
974
+ } else if (typeof rawTime === 'string' && rawTime.trim() !== '') {
975
+ resetsAt = rawTime.trim()
976
+ }
977
+ items.push({ label: tier, percent, resetsAt })
957
978
  }
958
979
  return { level: label(body?.data?.level), items }
959
980
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plugins/dsh-llm-hub",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "DSH 模型管理层:网关可达性探测、模型目录拉取、DeepSeek + MiniMax/智谱/Moonshot/StepFun 余额与配额、本月用量统计、余额预警、Provider 健康看板,再把本机装好的 codex / claude / agy 注册成子代理(装了才出现)。零运行时依赖,不改动 DSH 安装。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",