dsh-cost-meter 1.5.19 → 1.5.21

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 CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Per-conversation cost · daily totals · OpenCode Go subscription quota display · budget with usage percentage · official account balance · custom provider balance · balance progress bar · history · peak/off-peak pricing hours display (peak hours UTC 01:00–04:00, 06:00–10:00) · pre-switch popup & system-notification alerts for peak/off-peak changes (position / lead time / alert type configurable) · one-click price sync from the official docs · Codex-style token usage heat grid · multi-vendor model pricing (built-in 90+ model price catalog with auto-matching) · mainstream Coding Plan quota queries & display (Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / SCNet)
8
8
 
9
- [![version](https://img.shields.io/badge/version-1.5.19-4176E6)](https://github.com/Han-1413141/dsh-cost-meter)
9
+ [![version](https://img.shields.io/badge/version-1.5.21-4176E6)](https://github.com/Han-1413141/dsh-cost-meter)
10
10
  [![npm](https://img.shields.io/npm/v/dsh-cost-meter?label=npm)](https://www.npmjs.com/package/dsh-cost-meter)
11
11
  [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
12
12
  [![dsh](https://img.shields.io/badge/DeepSeek%20Harness-dsh--plugin-4176E6)](https://github.com/deepseek-ai/deepseek-harness)
@@ -40,7 +40,7 @@ English | [中文](README.md)
40
40
  | Budget settings | Settings page, top | Limit, period (today / month / cumulative / custom date range), used % |
41
41
  | Price table | Settings page | Per-model off-peak / peak prices (input/output shorthand supported; cache prices derived automatically); fully editable |
42
42
  | Peak/off-peak hours display | Settings / budget / today | Shows UTC peak hours 01:00–04:00 and 06:00–10:00 with the current tier; expanded view shows a peak/off-peak period strip (current period + countdown), collapsed (rail) view shows a vertical peak/off-peak progress bar; independently toggleable |
43
- | Peak/off-peak switch popup alert | Global overlay | A full-width bracketed popup appears when the next tier switch is within the configured lead time (default 2 minutes, 1–30), with an alert-colored badge distinguishing entering peak vs off-peak; position selectable (**bottom-right / screen center**), alert type selectable (entering peak / entering off-peak / both), one alert per switch point; optionally **sends a browser (system) notification** (so you still get alerted when the page is backgrounded; requires granting notification permission); configured in the peak pricing panel in Settings |
43
+ | Peak/off-peak switch popup alert | Global overlay | A full-width bracketed popup appears when the next tier switch is within the configured lead time (default 2 minutes, 1–30), with an alert-colored badge distinguishing entering peak vs off-peak; position selectable (**bottom-right / screen center**), alert type selectable (entering peak / entering off-peak / both), one alert per switch point; optionally **sends a browser (system) notification** (so you still get alerted when the page is backgrounded; requires granting notification permission); configured in the peak pricing panel in Settings, with a **one-click popup preview** (rendered by the real component — copy, position and notifications exactly as they will fire) |
44
44
  | Official price sync | Settings page | Fetches and parses the official pricing page, applies with one click |
45
45
  | UI language | Settings → Display settings | Simplified Chinese / English / Follow browser (auto); switches instantly and auto-saves |
46
46
  | AI price sync | [prompt](docs/AI-PRICE-SYNC-PROMPT.en.md) | DeepSeek official sync; other providers use the verified official price catalog and manual configuration |
@@ -193,22 +193,22 @@ Real captures from an actual DSH sidebar of the period strip and collapsed verti
193
193
  dsh plugin --profile web add dsh-cost-meter
194
194
  ```
195
195
 
196
- **PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.5.19`** — review the script before running):
196
+ **PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.5.21`** — review the script before running):
197
197
 
198
198
  ```powershell
199
- irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.19/install.ps1 | iex
199
+ irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.21/install.ps1 | iex
200
200
  ```
201
201
 
202
202
  **Or a plain command line** (the machine must already have pnpm and git; also pinned to the tag):
203
203
 
204
204
  ```sh
205
- dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.19
205
+ dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.21
206
206
  ```
207
207
 
208
208
  Without git, use the GitHub tag archive:
209
209
 
210
210
  ```sh
211
- dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.19.tar.gz
211
+ dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.21.tar.gz
212
212
  ```
213
213
 
214
214
  After installing, **restart** `dsh web` (plugin rows, the Typert manifest and the client bundle are all scanned at startup):
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  本会话费用 · 当日费用 · OpenCode Go 订阅额度显示 · 预算与已用百分比 · 官方账户余额 · 自定义 Provider 余额查询(可配任意 HTTP 端点) · 余额三段进度条 · 历史记录 · 峰谷计价时段显示(UTC 01:00–04:00、06:00–10:00 为峰时段) · 峰/谷切换前弹窗与系统通知提醒(位置/提前量/提醒类型可配) · 官方价格一键同步 · 类 Codex Token 用量热图 · 多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配) · 主流 Coding Plan 额度查询与显示(Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / SCNet 七家)
8
8
 
9
- [![version](https://img.shields.io/badge/version-1.5.19-4176E6)](https://github.com/Han-1413141/dsh-cost-meter)
9
+ [![version](https://img.shields.io/badge/version-1.5.21-4176E6)](https://github.com/Han-1413141/dsh-cost-meter)
10
10
  [![npm](https://img.shields.io/npm/v/dsh-cost-meter?label=npm)](https://www.npmjs.com/package/dsh-cost-meter)
11
11
  [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
12
12
  [![dsh](https://img.shields.io/badge/DeepSeek%20Harness-dsh--plugin-4176E6)](https://github.com/deepseek-ai/deepseek-harness)
@@ -41,7 +41,7 @@
41
41
  | 预算设置 | 设置页顶部 | 额度、周期(今日/本月/累计/自定义日期区间)、已用% |
42
42
  | 价格表 | 设置页 | 每模型 谷时/峰时 两档价格(支持 input/output 简写,缓存价自动补齐),增删改自由 |
43
43
  | 峰谷计价时段显示 | 设置页 / 预算 / 今日费用 | 显示 UTC 峰时段 01:00–04:00、06:00–10:00 与当前档位;展开态显示峰时/平价时段条(当前时段 + 倒计时),收起(rail)态显示竖向峰谷进度条,可单独开关 |
44
- | 峰/谷切换弹窗提醒 | 全局浮层 | 距进入峰/谷时段不足设定提前量(默认 2 分钟,1-30 可配)时全屏色条徽标弹窗(提醒色区分进入峰/谷);弹窗位置可选**右下角 / 屏幕中心**,提醒类型可选(进入峰 / 进入谷 / 峰和谷),同一切换点只提醒一次;可选**同步发送浏览器(系统)通知**(页面最小化也能收到,需授权通知权限),设置页峰谷计价面板内配置 |
44
+ | 峰/谷切换弹窗提醒 | 全局浮层 | 距进入峰/谷时段不足设定提前量(默认 2 分钟,1-30 可配)时全屏色条徽标弹窗(提醒色区分进入峰/谷);弹窗位置可选**右下角 / 屏幕中心**,提醒类型可选(进入峰 / 进入谷 / 峰和谷),同一切换点只提醒一次;可选**同步发送浏览器(系统)通知**(页面最小化也能收到,需授权通知权限);设置页峰谷计价面板内配置,并可**一键预览弹窗效果**(真实组件渲染,文案/位置/通知与实际触发完全一致) |
45
45
  | 官方价格同步 | 设置页 | 抓取解析官方定价页,一键应用 |
46
46
  | 界面语言 | 设置页 → 显示设置 | 简体中文 / English / 跟随浏览器(自动);切换即时生效并自动保存 |
47
47
  | AI 价格同步 | [提示词](docs/AI-PRICE-SYNC-PROMPT.md) | DeepSeek 官方同步;其他 provider 使用已核对的官方价格目录与手动配置 |
@@ -195,22 +195,22 @@
195
195
  dsh plugin --profile web add dsh-cost-meter
196
196
  ```
197
197
 
198
- **PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.5.19`**,建议先下载审阅再运行):
198
+ **PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.5.21`**,建议先下载审阅再运行):
199
199
 
200
200
  ```powershell
201
- irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.19/install.ps1 | iex
201
+ irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.5.21/install.ps1 | iex
202
202
  ```
203
203
 
204
204
  **或直接命令行**(机器上需已有 pnpm 与 git;同样固定到 tag):
205
205
 
206
206
  ```sh
207
- dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.19
207
+ dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.5.21
208
208
  ```
209
209
 
210
210
  没有 git 时可用 GitHub tag 打包直链:
211
211
 
212
212
  ```sh
213
- dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.19.tar.gz
213
+ dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.5.21.tar.gz
214
214
  ```
215
215
 
216
216
  安装后**重启** `dsh web`(插件行、Typert 清单与客户端 bundle 均在启动时扫描):
package/lib/client.js CHANGED
@@ -122,6 +122,7 @@ window.__ModuleLoader__.load({
122
122
  '.cm-peak-alert-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}',
123
123
  '.cm-peak-alert-body{color:var(--dsw-alias-label-secondary);line-height:1.55}',
124
124
  '.cm-peak-alert-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}',
125
+ '.cm-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}',
125
126
  '@keyframes cm-peak-alert-in{from{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}',
126
127
  '@keyframes cm-peak-alert-in-center{from{opacity:0;transform:translate(-50%,calc(-50% + 10px))}to{opacity:1;transform:translate(-50%,-50%)}}',
127
128
  '.cm-budget-head{display:flex;align-items:center;justify-content:space-between;gap:8px}',
@@ -455,6 +456,10 @@ window.__ModuleLoader__.load({
455
456
  peakAlertPositionCenter: '屏幕中心',
456
457
  peakAlertWebNotifyLabel: '同步发送系统通知',
457
458
  peakAlertWebNotifyHint: '开关后请在浏览器地址栏允许通知权限,才能在页面最小化/切走时收到提醒。',
459
+ peakAlertPreviewLabel: '预览弹窗',
460
+ peakAlertPreviewPeak: '预览 进入峰',
461
+ peakAlertPreviewOffPeak: '预览 进入谷',
462
+ peakAlertPreviewTag: '(预览)',
458
463
  peakPanelTitle: '峰谷计价与提示',
459
464
  peakNoticeHiddenHint: '提示已隐藏:需启用峰谷计价并开启「峰时高价时段显著提示」。',
460
465
  groupGeneral: '常规',
@@ -775,6 +780,10 @@ window.__ModuleLoader__.load({
775
780
  peakAlertPositionCenter: 'Screen center',
776
781
  peakAlertWebNotifyLabel: 'Also send a system notification',
777
782
  peakAlertWebNotifyHint: 'When enabled, allow notification permission in the browser address bar so you get alerted even when the page is minimized or backgrounded.',
783
+ peakAlertPreviewLabel: 'Preview the popup',
784
+ peakAlertPreviewPeak: 'Preview entering peak',
785
+ peakAlertPreviewOffPeak: 'Preview entering off-peak',
786
+ peakAlertPreviewTag: ' (preview)',
778
787
  peakPanelTitle: 'Peak/off-peak pricing & notice',
779
788
  peakNoticeHiddenHint: 'Notice hidden: enable peak/off-peak pricing and the “prominent notice” toggle.',
780
789
  groupGeneral: 'General',
@@ -2011,22 +2020,51 @@ window.__ModuleLoader__.load({
2011
2020
  : t('countdownMinute', { m: minutes })
2012
2021
  }
2013
2022
 
2023
+ // 预览通道事件名:设置页按钮/控制台经 window.cmPeakAlertPreview(kind) 触发真实组件。
2024
+ const PEAK_ALERT_PREVIEW_EVENT = 'cm-peak-alert-preview'
2025
+
2014
2026
  /**
2015
2027
  * 峰/谷切换前弹窗提醒:距下次相位切换不足配置提前量(默认 2 分钟)时弹浮层,
2016
2028
  * 位置可选屏幕右下角 / 屏幕中心;提醒类型按配置过滤(进入峰/进入谷/峰和谷);
2017
2029
  * 同一切换点只弹一次(手动关闭即记点),切换完成后浮层自然消失。
2018
2030
  * 若开启 Web 通知且有授权,还会在同一切换点向系统发送一次浏览器通知。
2031
+ * 预览:window.cmPeakAlertPreview('peak'|'offpeak') 用真实组件/文案/位置/通知
2032
+ * 渲染一次弹窗(峰谷计价启用时常驻监听,提醒开关关闭也可预览)。
2019
2033
  */
2020
2034
  function PeakAlert(props) {
2021
2035
  const costStore = props.useCost ? props.useCost(s => s) : undefined
2022
2036
  const config = costStore?.state?.config
2023
2037
  const [now, setNow] = useState(Date.now())
2024
2038
  const [dismissedAt, setDismissedAt] = useState(null)
2039
+ const [preview, setPreview] = useState(null)
2025
2040
  const notifiedAtRef = useRef(null)
2026
2041
  useEffect(() => {
2027
2042
  const timer = window.setInterval(() => setNow(Date.now()), 10000)
2028
2043
  return () => window.clearInterval(timer)
2029
2044
  }, [])
2045
+ // 预览通道:监听自定义事件(事件由 activate 顶层的 window.cmPeakAlertPreview 派发),
2046
+ // 组件挂载期间置在线标志,供 API 判断弹窗宿主是否可用。
2047
+ useEffect(() => {
2048
+ const onPreview = event => {
2049
+ const kind = event.detail?.kind === 'offpeak' ? 'offpeak' : 'peak'
2050
+ setPreview(kind)
2051
+ // 预览系统通知:遵循 Web 通知开关与授权,标题加预览标记。
2052
+ const cfg = costStore?.state?.config
2053
+ if (cfg?.peakAlertWebNotify === true && window.Notification && Notification.permission === 'granted') {
2054
+ const pt = makeT(resolveLocale(cfg.locale))
2055
+ try {
2056
+ new Notification(pt(kind === 'peak' ? 'peakAlertTitlePeak' : 'peakAlertTitleOffPeak') + pt('peakAlertPreviewTag'),
2057
+ { body: pt('peakAlertBody', { time: pt('countdownMinute', { m: 2 }), phase: pt(kind === 'peak' ? 'peakAlertPhasePeak' : 'peakAlertPhaseOffPeak') }) })
2058
+ } catch (_) { /* 通知被系统拒绝时静默 */ }
2059
+ }
2060
+ }
2061
+ window.addEventListener(PEAK_ALERT_PREVIEW_EVENT, onPreview)
2062
+ window.__cmPeakAlertLive = true
2063
+ return () => {
2064
+ window.removeEventListener(PEAK_ALERT_PREVIEW_EVENT, onPreview)
2065
+ window.__cmPeakAlertLive = false
2066
+ }
2067
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
2030
2068
  useEffect(() => {
2031
2069
  // Web 通知:每次 tick 自包含重算切换点,同一切换点只发一次。
2032
2070
  if (!config || config.peakAlertEnabled !== true || config.peakEnabled !== true) return
@@ -2048,21 +2086,35 @@ window.__ModuleLoader__.load({
2048
2086
  { body: t('peakAlertBody', { time: countdownText(wv, now, t), phase: t(intoPeak ? 'peakAlertPhasePeak' : 'peakAlertPhaseOffPeak') }) })
2049
2087
  } catch (_) { /* 通知被系统拒绝时静默 */ }
2050
2088
  }, [now]) // eslint-disable-line react-hooks/exhaustive-deps
2051
- if (!config || config.peakAlertEnabled !== true || config.peakEnabled !== true) return null
2052
- const effectiveAtMs = Date.parse(config.peakEffectiveAt || '')
2053
- if (Number.isFinite(effectiveAtMs) && now < effectiveAtMs) return null
2054
- const view = peakPhaseAt(now, Array.isArray(config.peakWindows) ? config.peakWindows : [])
2055
- if (view === null) return null
2056
- const target = config.peakAlertTarget === 'peak' || config.peakAlertTarget === 'offpeak' ? config.peakAlertTarget : 'both'
2057
- if (target !== 'both' && target !== (view.nextIntoPeak ? 'peak' : 'offpeak')) return null
2058
- const aheadMinutes = Number(config.peakAlertAhead)
2059
- const aheadMs = (Number.isFinite(aheadMinutes) && aheadMinutes >= 1 ? aheadMinutes : 2) * 60000
2060
- if (now < view.nextAtMs - aheadMs || now >= view.nextAtMs) return null
2061
- if (dismissedAt === view.nextAtMs) return null
2089
+ if (!config || config.peakEnabled !== true) return null
2090
+ // 真实弹窗判定:提醒开关开启 + 已生效 + 相位/类型/提前量窗口内 + 未手动关闭。
2091
+ let real = null
2092
+ if (config.peakAlertEnabled === true) {
2093
+ const effectiveAtMs = Date.parse(config.peakEffectiveAt || '')
2094
+ if (!(Number.isFinite(effectiveAtMs) && now < effectiveAtMs)) {
2095
+ const view = peakPhaseAt(now, Array.isArray(config.peakWindows) ? config.peakWindows : [])
2096
+ if (view !== null) {
2097
+ const target = config.peakAlertTarget === 'peak' || config.peakAlertTarget === 'offpeak' ? config.peakAlertTarget : 'both'
2098
+ const aheadMinutes = Number(config.peakAlertAhead)
2099
+ const aheadMs = (Number.isFinite(aheadMinutes) && aheadMinutes >= 1 ? aheadMinutes : 2) * 60000
2100
+ if ((target === 'both' || target === (view.nextIntoPeak ? 'peak' : 'offpeak'))
2101
+ && now >= view.nextAtMs - aheadMs && now < view.nextAtMs && dismissedAt !== view.nextAtMs) real = view
2102
+ }
2103
+ }
2104
+ }
2105
+ // 渲染:真实窗口激活用真实切换点;否则若有预览请求,用虚拟切换点(2 分钟后)。
2106
+ let view = null, intoPeak = false, dismiss = null
2107
+ if (real !== null) {
2108
+ view = real
2109
+ intoPeak = real.nextIntoPeak === true
2110
+ dismiss = () => setDismissedAt(real.nextAtMs)
2111
+ } else if (preview !== null) {
2112
+ view = { nextAtMs: now + 2 * 60000, nextIntoPeak: preview === 'peak' }
2113
+ intoPeak = preview === 'peak'
2114
+ dismiss = () => setPreview(null)
2115
+ } else return null
2062
2116
  const t = makeT(resolveLocale(config.locale))
2063
- const intoPeak = view.nextIntoPeak === true
2064
2117
  const position = config.peakAlertPosition === 'center' ? 'cm-peak-alert-center' : 'cm-peak-alert-corner'
2065
- const actionBtn = (className, label, fn) => el('button', { className, onClick: fn }, label)
2066
2118
  return el('div', { className: 'cm-peak-alert ' + position + ' ' + (intoPeak ? 'cm-peak-alert-peak' : 'cm-peak-alert-offpeak'), role: 'alert' },
2067
2119
  el('div', { className: 'cm-peak-alert-badge' }, t(intoPeak ? 'peakAlertBadgePeak' : 'peakAlertBadgeOffPeak')),
2068
2120
  el('div', { className: 'cm-peak-alert-title' }, t(intoPeak ? 'peakAlertTitlePeak' : 'peakAlertTitleOffPeak')),
@@ -2071,7 +2123,7 @@ window.__ModuleLoader__.load({
2071
2123
  phase: t(intoPeak ? 'peakAlertPhasePeak' : 'peakAlertPhaseOffPeak'),
2072
2124
  })),
2073
2125
  el('div', { className: 'cm-peak-alert-actions' },
2074
- actionBtn('cm-btn', t('peakAlertBtn'), () => setDismissedAt(view.nextAtMs))))
2126
+ el('button', { className: 'cm-btn', onClick: dismiss }, t('peakAlertBtn'))))
2075
2127
  }
2076
2128
 
2077
2129
  /** 展开态简洁样式:单行紧凑时段条——细轨道(左橙右蓝,非当前段淡化)+ 标记线 + 右侧倒计时文本。 */
@@ -2893,6 +2945,11 @@ window.__ModuleLoader__.load({
2893
2945
  }),
2894
2946
  el('span', null, t('peakAlertWebNotifyLabel'))),
2895
2947
  el('p', { className: 'cm-hint' }, t('peakAlertWebNotifyHint')))),
2948
+ el('div', { className: 'cm-field' },
2949
+ el('label', null, t('peakAlertPreviewLabel')),
2950
+ el('div', { className: 'cm-row' },
2951
+ el('button', { className: 'cm-btn', onClick: () => window.cmPeakAlertPreview?.('peak') }, t('peakAlertPreviewPeak')),
2952
+ el('button', { className: 'cm-btn', onClick: () => window.cmPeakAlertPreview?.('offpeak') }, t('peakAlertPreviewOffPeak')))),
2896
2953
  el('div', { className: 'cm-peak-preview' },
2897
2954
  draft?.peakEnabled !== false && draft?.peakNotice !== false
2898
2955
  ? peakNoticeEl(state, previewConfig, t)
@@ -4581,17 +4638,19 @@ window.__ModuleLoader__.load({
4581
4638
  })
4582
4639
  }
4583
4640
 
4584
- // 峰/谷切换前弹窗提醒:全局 fixed 浮层,挂在 dock 插槽常驻渲染,组件内部
4585
- // 再按配置门控(peakAlertEnabled + peakEnabled);开关变化时重挂/卸载。
4641
+ // 峰/谷切换前弹窗提醒:全局 fixed 浮层(fixed 定位与宿主位置无关),挂在常驻的
4642
+ // sidebar.footer.action 插槽——conversation.composer.dock 仅在有活跃会话的页面渲染,
4643
+ // 挂那里会导致 hero/设置页不弹提醒、预览按钮失效(issue:预览点了没反应)。
4644
+ // 组件内部再按配置门控(peakAlertEnabled + peakEnabled);开关变化时重挂/卸载。
4586
4645
  const peakAlertActive = { gen: 0, dispose: null }
4587
4646
  const registerPeakAlert = enabled => {
4588
4647
  if (peakAlertActive.dispose !== null) { peakAlertActive.dispose(); peakAlertActive.dispose = null }
4589
4648
  peakAlertActive.gen += 1
4590
4649
  const gen = peakAlertActive.gen
4591
4650
  if (!enabled) return
4592
- slots.inject('conversation.composer.dock', () => {
4651
+ slots.inject('sidebar.footer.action', () => {
4593
4652
  if (peakAlertActive.gen !== gen) return
4594
- const dispose = slots.register({ name: 'conversation.composer.dock', id: 'cost-meter-peak-alert', order: 8, inject: injected }, PeakAlert)
4653
+ const dispose = slots.register({ name: 'sidebar.footer.action', id: 'cost-meter-peak-alert', order: 0, inject: injected }, PeakAlert)
4595
4654
  if (peakAlertActive.gen !== gen) { dispose(); return }
4596
4655
  peakAlertActive.dispose = dispose
4597
4656
  return () => {
@@ -4601,6 +4660,17 @@ window.__ModuleLoader__.load({
4601
4660
  })
4602
4661
  }
4603
4662
 
4663
+ // 预览 API 在 activate 顶层注册(不依赖任何插槽挂载):设置页按钮与控制台均经
4664
+ // 此入口派发事件;弹窗宿主组件挂载时置 __cmPeakAlertLive,未挂载时给出可诊断提示。
4665
+ window.cmPeakAlertPreview = kind => {
4666
+ if (window.__cmPeakAlertLive !== true) {
4667
+ // eslint-disable-next-line no-console
4668
+ console.warn('[dsh-cost-meter] 弹窗组件未挂载:需启用峰谷计价并重启 dsh web 后再预览。')
4669
+ return
4670
+ }
4671
+ window.dispatchEvent(new CustomEvent(PEAK_ALERT_PREVIEW_EVENT, { detail: { kind: kind === 'offpeak' ? 'offpeak' : 'peak' } }))
4672
+ }
4673
+
4604
4674
  // 设置页「费用/Cost」分节:语言变化时撤销旧注册并重建,让侧边栏标签同步。
4605
4675
  const sectionActive = { gen: 0, dispose: null }
4606
4676
  const registerSection = locale => {
@@ -4696,7 +4766,8 @@ window.__ModuleLoader__.load({
4696
4766
  registerCorner(cornerEnabled)
4697
4767
  lastCorner = cornerEnabled
4698
4768
  }
4699
- const peakAlertOn = state?.config?.peakAlertEnabled !== false && state?.config?.peakEnabled === true
4769
+ // 峰谷计价启用即常驻挂载(提醒开关关闭时也保留组件,供设置页预览弹窗)。
4770
+ const peakAlertOn = state?.config?.peakEnabled === true
4700
4771
  if (peakAlertOn !== lastPeakAlert) {
4701
4772
  registerPeakAlert(peakAlertOn)
4702
4773
  lastPeakAlert = peakAlertOn
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-cost-meter",
3
- "version": "1.5.19",
3
+ "version": "1.5.21",
4
4
  "description": "DeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(7 家,含 SCNet Token Plan 本地 Credits 计量)、自定义 Provider 余额查询(可配任意 HTTP 端点)与余额进度条、峰谷计价时段显示与切换前弹窗/系统通知提醒(位置/提前量/类型可配),界面中英双语。Session cost tracking plugin for DeepSeek Harness: per-conversation cost, daily totals, history and official price sync, with multi-vendor model pricing (built-in 90+ model catalog with auto-matching), Coding Plan quota queries & display for 7 vendors (incl. local credits metering for the SCNet Token Plan), custom provider balance lookup (configurable HTTP endpoint) and balance progress bar, plus peak/off-peak pricing display with pre-switch popup & system-notification alerts (position / lead time / type configurable), in a bilingual (Chinese/English) UI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",