@rayadesu/dsh-client-ui-billing 0.3.10 → 0.3.11

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record both hashes with:
4
4
  # git hash-object README.md README.zh.md
5
- README.md: cd35c163925d48ff81774fefd45ea8deb06d59bd
6
- README.zh.md: 7d06d48a6da7337faa1acea887b59a6624a81ea3
5
+ README.md: aa8362e8ae5fd116cfff977101ddb4ec0cc0bd31
6
+ README.zh.md: a10b50f901fea3fd26b615c41d7318eda55c43d7
package/README.md CHANGED
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
4
4
 
5
5
  Web billing feature owner: contributes one entry to `conversation.session.header.utilities` that mounts its own `billing` Remote and renders the DeepSeek account balance, this session's billed spend (read from the host-pushed `billingTodaySpend` projection, with `billing/getSessionSpend` as the fallback when the projection registry is absent), today's spend across every session (`billing/getTodaySpend`), today's per-session ranking (`billing/getTodaySessionsSpend`), and per-turn costs (`billing/getSessionTurnSpends`) as a top-right label box. The host half of the capability lives in [`dsh-llm-billing`](../llm-billing/README.md), which owns the `/user/balance` transport, the peak/off-peak pricing table, and the `billing` Remote namespace; this package mounts that Remote and renders what it returns.
6
6
 
7
- The trigger shows two lines — the remaining balance and this conversation's billed spend ("本轮对话花费 ¥X") — and renders immediately from the last settled balance (kept per plugin instance), revalidating in the background; before the first balance ever lands it renders nothing. Clicking it opens a label box with the remaining amount, this session's billed spend (本会话花费, priced per usage sample at the official peak/off-peak rate — peak windows apply weekdays Mon–Fri only, weekends are off-peak; a forked session prices only its own post-fork work) with today's all-session spend beside it (今日共花费), one priced row per model with the cache-hit / cache-miss-input / output cost breakdown ("缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z"), a manual refresh action, and a spend disclaimer. The remaining-amount row (`API 剩余金额`) renders larger than the spend rows below it. Below the model rows the panel ends with the **today session-spend ranking** (`今日会话花费`): sessions sorted by today's cost, highest first, each row a fixed right-aligned rank column, then the same middot separator the cost breakdown uses ("·" with the same size, tone, and spacing), then the session's durable title (renames sync automatically), then its amount — the title keeps its indented rank column; at most `SESSION_RANKING_LIMIT` (10) rows render, with a "…还有 N 个会话" hint for the rest. Refreshing keeps the last values visible rather than blanking them, and a refresh failure retains the last good value. A session or day without priced usage shows a "no usage recorded" word rather than a fabricated figure. A failure — no API key configured, a rejected credential, a transport error — renders a muted "Balance unavailable" word whose tooltip carries the Remote's own error message.
7
+ The trigger shows two lines — the remaining balance and this conversation's billed spend ("本轮对话花费 ¥X") — and renders immediately from the last settled balance (kept per plugin instance), revalidating in the background; before the first balance ever lands it renders nothing. Clicking it opens a label box with the remaining amount, this session's billed spend (本会话花费, priced per usage sample at the official peak/off-peak rate — peak windows apply weekdays Mon–Fri only, weekends are off-peak; a forked session prices only its own post-fork work) with today's all-session spend beside it (今日); a bare parenthesized amount rides right after the session amount ((¥X)) — that is **this session's share of today**, taken from the already-fetched today-session ranking and matched by the current session id, so it is neither the ranking's first row nor the day's total: it renders (—) while that ranking has not settled, and (¥0) once it settled without a row for this session; next come one priced row per model with the cache-hit / cache-miss-input / output cost breakdown ("缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z"), a manual refresh action, and a short spend disclaimer on the `?` button (only DeepSeek and MiMo models are estimated, each message priced at its own peak/off-peak rate, followed, on the next line (flush left), by a bare version like `v0.3.11` — stamped in at build time from this package's manifest, so a report can name the build; the full rate schedule lives in the host README, because the DSH tooltip bubble fits a short label only, is clipped at the viewport edge when long, and cannot be hovered — the hint is placed below the button, the side whose viewport fit can flip). The remaining-amount row (`API 剩余金额`) renders larger than the spend rows below it (本会话花费 with its parenthesized today share, and 今日). Below the model rows the panel ends with the **today session-spend ranking** (`今日会话花费`): sessions sorted by today's cost, highest first, each row a fixed right-aligned rank column, then the same middot separator the cost breakdown uses ("·" with the same size, tone, and spacing), then the session's durable title (renames sync automatically), then its amount — the title keeps its indented rank column; at most `SESSION_RANKING_LIMIT` (10) rows render, with a "…还有 N 个会话" hint for the rest. Refreshing keeps the last values visible rather than blanking them, and a refresh failure retains the last good value. A session or day without priced usage shows a "no usage recorded" word rather than a fabricated figure. A failure — no API key configured, a rejected credential, a transport error — renders a muted "Balance unavailable" word whose tooltip carries the Remote's own error message.
8
8
 
9
9
  The package also contributes the **turn-cost entry** (`billing-turn-cost`, order 20) to ui-chat's `conversation.chat.assistant-actions` list slot — the same strip `ui-message-feedback` uses, so entries coexist; the entry's own CSS `order: 1` sorts it after every order-0 sibling, so the amount lands at the **end** of the actions row (after the clock). The entry renders a plain static `¥X` (no icon, no label, no interaction) for each completed turn's closing assistant message, resolved from a shared per-session `messageId → cost` map (`billing/getSessionTurnSpends`, one fetch per session, coalesced across concurrently mounting rows; a message id the map does not know triggers exactly one refetch for a newly completed turn). Turns that priced to zero (no DeepSeek usage) and failed loads render nothing, so a Remote outage never clutters the row.
10
10
 
@@ -15,7 +15,7 @@ The badge is account-level even though the slot is session-scoped: the header ut
15
15
  The spends follow the conversation and the balance is cached, not polled:
16
16
 
17
17
  - **Session spend is pushed, not fetched** — the host folds each session's spend into the client-visible `billingTodaySpend` projection and pushes every change; the badge subscribes with the framework `useProjection` seat, so the session line is live with zero Remote calls while the projection registry is composed. When the key is absent, the same line falls back to `billing/getSessionSpend`.
18
- - **Today's spend follows settled turns, debounced** — the component subscribes to the current session's running flag through the framework `useSession` seat. When a prompt turn settles (the flag flips back to idle) it recomputes today's spend (`billing/getTodaySpend`); the recompute is debounced for two seconds, so a burst of turns (an agent continuing across turns) prices once instead of once per turn. The ranking (`billing/getTodaySessionsSpend`) is fetched only while the detail panel is open (and on refresh), so a badge that stays closed never pays for the all-session scan.
18
+ - **Today's spend follows settled turns, debounced** — the component subscribes to the current session's running flag through the framework `useSession` seat. When a prompt turn settles (the flag flips back to idle) it recomputes today's spend (`billing/getTodaySpend`); the recompute is debounced for two seconds, so a burst of turns (an agent continuing across turns) prices once instead of once per turn. The ranking (`billing/getTodaySessionsSpend`) is fetched only while the detail panel is open (and on refresh), so a badge that stays closed never pays for the all-session scan; the parenthesized today share on the 本会话花费 row reads that same fetch, so it costs no extra request and moves with the ranking ((—) until the first one settles).
19
19
  - **Balance is TTL-cached** — the account balance is fetched on mount, on session switch, and on the explicit refresh action. The host reuses one snapshot for up to 15 seconds (`force` on the manual refresh bypasses it) and aborts a request after 5 seconds; the browser keeps the last settled value so a session switch renders the amount immediately. There is still no polling and no automatic refetch.
20
20
  - **Refresh keeps the last values** — an in-flight refresh leaves the previous values on screen, and a failed refresh retains the last good value instead of blanking it. The refresh action calls `billing/getBalance(true)`, `billing/getTodaySpend(true)`, and `billing/getTodaySessionsSpend(true)`, which bypass the host-side caches; the turn-triggered recompute and the mount/session-switch reads use the cached path.
21
21
 
package/README.zh.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Web 额度特性的归属方:向 `conversation.session.header.utilities` 贡献一个条目,自己挂载 `billing` Remote,并渲染 DeepSeek 账户余额、本会话的计费花费(读宿主推送的 `billingTodaySpend` 投影,投影注册表不存在时回退 `billing/getSessionSpend`)、今日所有会话的计费花费(`billing/getTodaySpend`)、今日按会话的排行(`billing/getTodaySessionsSpend`)与各回合花费(`billing/getSessionTurnSpends`),在右上角渲染成一个标签框。该能力的宿主侧在 [`dsh-llm-billing`](../llm-billing/README.md),它拥有 `/user/balance` 传输、峰谷计价表与 `billing` Remote 命名空间;本包挂载那个 Remote 并渲染它返回的内容。
6
6
 
7
- 触发器显示两行——剩余额度与本轮对话的计费花费(「本轮对话花费 ¥X」)——有上次已落定的余额就立即渲染(按插件实例保留),后台校验;首次余额尚未到达时不渲染任何东西。点击后展开一个标签框:剩余金额、本会话的计费花费(本会话花费,按官方峰/谷单价逐条用量样本计价——高峰窗口仅周一至周五适用,周末按低谷价——分叉会话只按分叉后的自有工作计价——右侧并列今日所有会话的合计「今日共花费」)、每个模型一行的花费及其缓存命中/未命中输入/输出分项(「缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z」)、手动刷新按钮与花费说明。剩余金额行(`API 剩余金额`)字号大于下方花费行。模型行下方是**今日会话花费排行**(`今日会话花费`):按今日花费从高到低排列的会话列表,每行以右对齐的名次列开头,后接与花费分项相同的小圆点分隔符(「·」,字号、色调与间距一致),再是会话的持久标题(重命名自动同步),最后是金额——标题在名次列后缩进对齐;最多渲染 `SESSION_RANKING_LIMIT`(10)行,其余以「…还有 N 个会话」提示。刷新时旧值不消失,刷新失败保留上一次有效值。没有可计价消耗的会话或日子显示「暂无消耗记录」而不是编造数字。失败——未配置 API key、凭据被拒绝、传输错误——渲染弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
7
+ 触发器显示两行——剩余额度与本轮对话的计费花费(「本轮对话花费 ¥X」)——有上次已落定的余额就立即渲染(按插件实例保留),后台校验;首次余额尚未到达时不渲染任何东西。点击后展开一个标签框:剩余金额、本会话的计费花费(「本会话花费」,按官方峰/谷单价逐条用量样本计价——高峰窗口仅周一至周五适用,周末按低谷价——分叉会话只按分叉后的自有工作计价——同行右侧并列今日所有会话的合计「今日」;本会话花费金额后紧跟一个括号括起来的纯金额(`(¥X)`),那是**本会话今日份**的花费,取自已拉取的今日会话排行、按当前会话 id 匹配,所以既不是排行首行、也不是当日合计——排行未落定时显示 `(—)`,落定但该会话没有行时显示 `(¥0)`)、每个模型一行的花费及其缓存命中/未命中输入/输出分项(「缓存命中 ¥X · 未命中输入 ¥Y · 输出 ¥Z」)、手动刷新按钮,以及「?」按钮上的一句简短花费说明(只估算 DeepSeek 与 MiMo 模型、按每条消息自身时刻的峰谷单价计价,下一行顶格是纯版本号(如 `v0.3.11`)——构建期从本包 manifest 注入,便于反馈时指明所用版本;费率明细在宿主 README —— DSH 的悬停气泡只装得下短文案,过长会在视口边缘被裁掉且无法悬停阅读,所以说明放在按钮下方,纵向适配可翻转的那一侧)。剩余金额行(`API 剩余金额`)字号大于下方花费行(`本会话花费` 与其括号金额、`今日`)。模型行下方是**今日会话花费排行**(`今日会话花费`):按今日花费从高到低排列的会话列表,每行以右对齐的名次列开头,后接与花费分项相同的小圆点分隔符(「·」,字号、色调与间距一致),再是会话的持久标题(重命名自动同步),最后是金额——标题在名次列后缩进对齐;最多渲染 `SESSION_RANKING_LIMIT`(10)行,其余以「…还有 N 个会话」提示。刷新时旧值不消失,刷新失败保留上一次有效值。没有可计价消耗的会话或日子显示「暂无消耗记录」而不是编造数字。失败——未配置 API key、凭据被拒绝、传输错误——渲染弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
8
8
 
9
9
  本包还贡献**本轮花费条目**(`billing-turn-cost`,order 20)到 ui-chat 的 `conversation.chat.assistant-actions` list slot——与 `ui-message-feedback` 同一条操作条,多条目共存;条目自身的 CSS `order: 1` 把它排到所有 order-0 兄弟之后,金额因此落在操作行**行尾**(时钟之后)。它为每个已完成回合的收尾助手消息渲染一个纯静态 `¥X`(无图标、无文字标签、不可点击),金额来自每会话共享的 `messageId → 金额` 映射(`billing/getSessionTurnSpends`,每会话只拉一次,并发挂载的行共享同一请求;映射里没有的消息 id 只触发一次重拉,对应当前刚完成的回合)。回合没有 DeepSeek 用量(花费为 0)或加载失败时不渲染,Remote 故障不会污染操作行。
10
10
 
@@ -15,7 +15,7 @@ Web 额度特性的归属方:向 `conversation.session.header.utilities` 贡
15
15
  花费跟随会话、额度带缓存但不轮询:
16
16
 
17
17
  - **本会话花费靠推送,不靠拉取** —— 宿主把每个会话的花费折进客户端可见的 `billingTodaySpend` 投影并推送每次变化;徽标用框架 `useProjection` 座位订阅,因此投影注册表装配时该行零 Remote 调用、实时更新。键不存在时同一行回退到 `billing/getSessionSpend`。
18
- - **今日共花费随回合结束更新(去抖)** —— 组件通过框架 `useSession` 座位订阅当前会话的「运行中」标志。每当一轮 prompt 回合结束(标志翻回空闲)就重算**今日共花费**(`billing/getTodaySpend`);重算有 2 秒去抖,回合突发(agent 连续多轮)只计价一次。**排行**(`billing/getTodaySessionsSpend`)只在详情面板打开时(以及手动刷新时)拉取,徽标一直关着就不会为全量会话扫描买单。
18
+ - **今日花费随回合结束更新(去抖)** —— 组件通过框架 `useSession` 座位订阅当前会话的「运行中」标志。每当一轮 prompt 回合结束(标志翻回空闲)就重算**今日**(`billing/getTodaySpend`);重算有 2 秒去抖,回合突发(agent 连续多轮)只计价一次。**排行**(`billing/getTodaySessionsSpend`)只在详情面板打开时(以及手动刷新时)拉取,徽标一直关着就不会为全量会话扫描买单;「本会话花费」行括号里的今日份金额也来自这次排行读取,所以它不额外发请求,代价是与排行同进同退(面板刚打开、排行未落定时先是 `(—)`)。
19
19
  - **额度带 TTL 缓存** —— 账户余额在挂载、切换会话、点刷新按钮时拉取。宿主 15 秒内复用同一份快照(手动刷新带 `force` 绕过),单次请求 5 秒超时;浏览器保留最后一次已落定值,切会话时立即渲染。仍然**没有轮询、不会自动重拉**。
20
20
  - **刷新保留旧值** —— 刷新在途时旧值留在屏幕上,刷新失败保留上一次有效值,不清空。刷新按钮调用 `billing/getBalance(true)`、`billing/getTodaySpend(true)` 与 `billing/getTodaySessionsSpend(true)`,绕过宿主侧缓存;回合结束触发的重算与挂载/切换会话的读取走缓存路径。
21
21
 
package/lib/client.js CHANGED
@@ -4365,8 +4365,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4365
4365
  return `¥${amount.toFixed(4).replace(/\.?0+$/, "")}`;
4366
4366
  }
4367
4367
  //#endregion
4368
+ //#region lib/types/client/version.js
4369
+ /**
4370
+ * The plugin's own version, reported in the spend-hint bubble so a report can
4371
+ * name the build actually in use.
4372
+ *
4373
+ * The value is stamped in at build time rather than read at runtime: a browser
4374
+ * bundle has no manifest to read. `packages/tsdown.client.ts` defines
4375
+ * `__DSH_PLUGIN_VERSION__` from this package's own `package.json` (the three
4376
+ * published packages share one version), and `vitest.config.ts` defines the
4377
+ * same constant from the same manifest so the suites see what a release ships.
4378
+ * The `typeof` guard keeps a source run without either definition (a foreign
4379
+ * bundler, a bare `tsc` check) from throwing on the undeclared global.
4380
+ * @module @rayadesu/dsh-client-ui-billing/version
4381
+ */
4382
+ /** The installed plugin version, e.g. `0.3.10`; `dev` when nothing stamped one. */
4383
+ const PLUGIN_VERSION = "0.3.11";
4384
+ //#endregion
4368
4385
  //#region \0dsh-css:C:\Users\admin\Desktop\me\code\DeepSeek-Harness-chat-billing\packages\ui-billing\src\client\BalanceBadge.module.css.mjs
4369
- const css$1 = "._4_Wqcq_root{position:relative}._4_Wqcq_trigger{background:var(--dsw-alias-fill-l2);min-height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;border:0;border-radius:8px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}._4_Wqcq_trigger:hover,._4_Wqcq_trigger:focus-visible{color:var(--dsw-alias-label-primary)}._4_Wqcq_trigger svg{transition:transform .12s}._4_Wqcq_chevronOpen{transform:rotate(180deg)}._4_Wqcq_triggerLines{text-align:left;flex-direction:column;align-items:flex-start;gap:0;display:flex}._4_Wqcq_linePrimary{font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}._4_Wqcq_lineSecondary{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:15px}._4_Wqcq_unavailable{color:var(--dsw-alias-label-tertiary)}._4_Wqcq_inlineIcon{color:var(--dsw-alias-label-tertiary);flex:none}._4_Wqcq_spinning{color:var(--dsw-alias-label-tertiary);flex:none;animation:.8s linear infinite _4_Wqcq_spin}@keyframes _4_Wqcq_spin{to{transform:rotate(360deg)}}._4_Wqcq_panel{z-index:100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu);width:336px;max-width:min(360px,100vw - 32px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:2px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);right:0}._4_Wqcq_amountRow{border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_amountLabel{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:13px;line-height:18px}._4_Wqcq_amountRow>._4_Wqcq_amountLabel{font-size:15px;line-height:22px}._4_Wqcq_spendRow{border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_costRow{align-items:center;padding:2px 8px 6px;display:flex}._4_Wqcq_costBreakdown{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}._4_Wqcq_amountActions{align-items:center;gap:2px;display:inline-flex}._4_Wqcq_infoButton,._4_Wqcq_refreshButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}._4_Wqcq_infoButton:hover,._4_Wqcq_infoButton:focus-visible,._4_Wqcq_refreshButton:hover,._4_Wqcq_refreshButton:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}._4_Wqcq_modelRow{border-radius:8px;justify-content:space-between;align-items:baseline;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_modelName{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}._4_Wqcq_tasks{text-align:right;min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;overflow-wrap:anywhere;font-size:12px;line-height:18px}._4_Wqcq_ranking{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:0;padding-top:2px;display:flex}._4_Wqcq_rankingTitle{color:var(--dsw-alias-label-secondary);padding:6px 8px 2px;font-size:12px;line-height:18px}._4_Wqcq_rankingRow{border-radius:8px;align-items:baseline;gap:0;min-width:0;padding:3px 8px;display:flex}._4_Wqcq_rankingRow:hover{background:var(--dsw-alias-fill-l2)}._4_Wqcq_rankingIndex{width:16px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;text-align:right;flex:none;font-size:11px;line-height:16px}._4_Wqcq_rankingDot{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:pre;flex:none;font-size:11px;line-height:16px}._4_Wqcq_rankingName{min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:11px;line-height:16px;overflow:hidden}._4_Wqcq_rankingAmount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:8px;font-size:11px;line-height:16px}._4_Wqcq_rankingMore{color:var(--dsw-alias-label-tertiary);padding:2px 8px 4px;font-size:11px;line-height:16px}";
4386
+ const css$1 = "._4_Wqcq_root{position:relative}._4_Wqcq_trigger{background:var(--dsw-alias-fill-l2);min-height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;border:0;border-radius:8px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}._4_Wqcq_trigger:hover,._4_Wqcq_trigger:focus-visible{color:var(--dsw-alias-label-primary)}._4_Wqcq_trigger svg{transition:transform .12s}._4_Wqcq_chevronOpen{transform:rotate(180deg)}._4_Wqcq_triggerLines{text-align:left;flex-direction:column;align-items:flex-start;gap:0;display:flex}._4_Wqcq_linePrimary{font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}._4_Wqcq_lineSecondary{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:15px}._4_Wqcq_unavailable{color:var(--dsw-alias-label-tertiary)}._4_Wqcq_inlineIcon{color:var(--dsw-alias-label-tertiary);flex:none}._4_Wqcq_spinning{color:var(--dsw-alias-label-tertiary);flex:none;animation:.8s linear infinite _4_Wqcq_spin}@keyframes _4_Wqcq_spin{to{transform:rotate(360deg)}}._4_Wqcq_panel{z-index:100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu);width:336px;max-width:min(360px,100vw - 32px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:2px;padding:6px;display:flex;position:absolute;top:calc(100% + 6px);right:0}._4_Wqcq_amountRow{border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_amountLabel{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:13px;line-height:18px}._4_Wqcq_amountRow>._4_Wqcq_amountLabel{font-size:15px;line-height:22px}._4_Wqcq_spendToday{color:var(--dsw-alias-label-tertiary);font-size:12px}._4_Wqcq_spendRow{border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_costRow{align-items:center;padding:2px 8px 6px;display:flex}._4_Wqcq_costBreakdown{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}._4_Wqcq_amountActions{align-items:center;gap:2px;display:inline-flex}._4_Wqcq_infoButton,._4_Wqcq_refreshButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}._4_Wqcq_infoButton:hover,._4_Wqcq_infoButton:focus-visible,._4_Wqcq_refreshButton:hover,._4_Wqcq_refreshButton:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}._4_Wqcq_modelRow{border-radius:8px;justify-content:space-between;align-items:baseline;gap:8px;padding:6px 8px;display:flex}._4_Wqcq_modelName{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}._4_Wqcq_tasks{text-align:right;min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;overflow-wrap:anywhere;font-size:12px;line-height:18px}._4_Wqcq_ranking{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:0;padding-top:2px;display:flex}._4_Wqcq_rankingTitle{color:var(--dsw-alias-label-secondary);padding:6px 8px 2px;font-size:12px;line-height:18px}._4_Wqcq_rankingRow{border-radius:8px;align-items:baseline;gap:0;min-width:0;padding:3px 8px;display:flex}._4_Wqcq_rankingRow:hover{background:var(--dsw-alias-fill-l2)}._4_Wqcq_rankingIndex{width:16px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;text-align:right;flex:none;font-size:11px;line-height:16px}._4_Wqcq_rankingDot{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:pre;flex:none;font-size:11px;line-height:16px}._4_Wqcq_rankingName{min-width:0;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:11px;line-height:16px;overflow:hidden}._4_Wqcq_rankingAmount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:8px;font-size:11px;line-height:16px}._4_Wqcq_rankingMore{color:var(--dsw-alias-label-tertiary);padding:2px 8px 4px;font-size:11px;line-height:16px}";
4370
4387
  const tagId$1 = "@rayadesu/dsh-client-ui-billing/BalanceBadge.module.css";
4371
4388
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
4372
4389
  const tag = document.createElement("style");
@@ -4400,6 +4417,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4400
4417
  "refreshButton": "_4_Wqcq_refreshButton",
4401
4418
  "root": "_4_Wqcq_root",
4402
4419
  "spendRow": "_4_Wqcq_spendRow",
4420
+ "spendToday": "_4_Wqcq_spendToday",
4403
4421
  "spin": "_4_Wqcq_spin",
4404
4422
  "spinning": "_4_Wqcq_spinning",
4405
4423
  "tasks": "_4_Wqcq_tasks",
@@ -4408,7 +4426,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4408
4426
  "unavailable": "_4_Wqcq_unavailable"
4409
4427
  };
4410
4428
  /** The detail box opened from the badge trigger. */
4411
- function BalancePanel({ amount, spend, todaySpend, sessionsSpend, refreshing, onRefresh, t }) {
4429
+ function BalancePanel({ amount, sessionId, spend, todaySpend, sessionsSpend, refreshing, onRefresh, t }) {
4430
+ const sessionToday = sessionsSpend === null ? void 0 : sessionsSpend.sessions.find((row) => row.sessionId === sessionId)?.total ?? 0;
4412
4431
  return (0, react_jsx_runtime.jsxs)("div", {
4413
4432
  className: BalanceBadge_module_css_default.panel,
4414
4433
  role: "dialog",
@@ -4422,9 +4441,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4422
4441
  }), (0, react_jsx_runtime.jsxs)("span", {
4423
4442
  className: BalanceBadge_module_css_default.amountActions,
4424
4443
  children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
4425
- label: t("info.hint"),
4444
+ label: t("info.hint", { version: PLUGIN_VERSION }),
4445
+ side: "bottom",
4426
4446
  delayMs: 200,
4427
- maxWidth: 340,
4447
+ maxWidth: 300,
4428
4448
  children: (0, react_jsx_runtime.jsx)("button", {
4429
4449
  type: "button",
4430
4450
  className: BalanceBadge_module_css_default.infoButton,
@@ -4443,9 +4463,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4443
4463
  }),
4444
4464
  (0, react_jsx_runtime.jsxs)("div", {
4445
4465
  className: BalanceBadge_module_css_default.spendRow,
4446
- children: [(0, react_jsx_runtime.jsx)("span", {
4466
+ children: [(0, react_jsx_runtime.jsxs)("span", {
4447
4467
  className: BalanceBadge_module_css_default.amountLabel,
4448
- children: t("label.sessionSpend", { amount: spend === null ? "—" : spend.models.length === 0 ? t("stat.none") : formatSpend(spend.total) })
4468
+ children: [t("label.sessionSpend", { amount: spend === null ? "—" : spend.models.length === 0 ? t("stat.none") : formatSpend(spend.total) }), (0, react_jsx_runtime.jsx)("span", {
4469
+ className: BalanceBadge_module_css_default.spendToday,
4470
+ children: t("label.sessionSpend.today", { amount: sessionToday === void 0 ? "—" : formatSpend(sessionToday) })
4471
+ })]
4449
4472
  }), (0, react_jsx_runtime.jsx)("span", {
4450
4473
  className: BalanceBadge_module_css_default.amountLabel,
4451
4474
  children: t("label.todaySpend", { amount: todaySpend === null ? "—" : todaySpend.models.length === 0 ? t("stat.none") : formatSpend(todaySpend.total) })
@@ -4731,6 +4754,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4731
4754
  t
4732
4755
  }), open ? (0, react_jsx_runtime.jsx)(BalancePanel, {
4733
4756
  amount,
4757
+ sessionId,
4734
4758
  spend,
4735
4759
  todaySpend,
4736
4760
  sessionsSpend,
@@ -4859,7 +4883,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4859
4883
  "trigger.conversationSpend": "本轮对话花费:{amount}",
4860
4884
  "label.amount": "API 剩余金额:{amount}",
4861
4885
  "label.sessionSpend": "本会话花费:{amount}",
4862
- "label.todaySpend": "今日共花费:{amount}",
4886
+ "label.sessionSpend.today": "{amount}",
4887
+ "label.todaySpend": "今日:{amount}",
4863
4888
  "label.cost.hit": "缓存命中 {amount}",
4864
4889
  "label.cost.input": "未命中输入 {amount}",
4865
4890
  "label.cost.output": "输出 {amount}",
@@ -4868,7 +4893,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4868
4893
  "state.unavailable": "额度不可用",
4869
4894
  "action.refresh": "刷新",
4870
4895
  "info.aria": "花费说明",
4871
- "info.hint": "仅能预估 DeepSeek MiMo 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。基础费率按 DeepSeek 8 月 17 日实行的标准,其中 flash 系列(V4.1 Flash / V4 Flash / Vision Exp)自 9 月 10 日 12:00 起执行新价(谷时 0.02 / 1.0 / 4.0 元每百万 token,峰时为其两倍),该时刻之前的消息仍按原价;V4 Pro 自 9 月 14 日 12:00 起改由 V4.1 Flash 服务并按其实施费率计费。高峰时段为北京时间周一至周五 9:00–12:00、14:00–18:00,周末全天按低谷价计费;MiMo 系列不区分峰谷,统一费率。",
4896
+ "info.hint": "估算:仅 DeepSeek MiMo 模型,按每条消息自身时刻的峰谷官方单价计价(高峰:工作日 9:00–12:00、14:00–18:00)。\nv{version}",
4872
4897
  "badge.aria": "DeepSeek 额度:{amount}",
4873
4898
  "panel.aria": "DeepSeek 额度详情",
4874
4899
  "label.sessionRanking": "今日会话花费",
@@ -4880,7 +4905,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4880
4905
  "trigger.conversationSpend": "This conversation: {amount}",
4881
4906
  "label.amount": "API balance: {amount}",
4882
4907
  "label.sessionSpend": "This session: {amount}",
4883
- "label.todaySpend": "Today total: {amount}",
4908
+ "label.sessionSpend.today": " ({amount})",
4909
+ "label.todaySpend": "Today: {amount}",
4884
4910
  "label.cost.hit": "Cache hit {amount}",
4885
4911
  "label.cost.input": "Missed input {amount}",
4886
4912
  "label.cost.output": "Output {amount}",
@@ -4889,7 +4915,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4889
4915
  "state.unavailable": "Balance unavailable",
4890
4916
  "action.refresh": "Refresh",
4891
4917
  "info.aria": "About this spend",
4892
- "info.hint": "Only DeepSeek and MiMo models are estimated. This session's spend is priced per message at the rate of its Beijing-time peak/off-peak hour: cache-hit input, cache-miss input (including cache writes), and output (including reasoning) are billed separately. The base rates follow DeepSeek's August 17 pricing; the flash series (V4.1 Flash, V4 Flash, Vision Exp) was re-priced from September 10, 12:00 Beijing time (off-peak 0.02 / 1.0 / 4.0 CNY per 1M tokens, peak at twice those prices) while messages from before that instant keep the earlier rates, and V4 Pro switches to V4.1 Flash and its rates from September 14, 12:00 Beijing time. Peak hours are Beijing time Monday–Friday 09:00–12:00 and 14:00–18:00, and weekends are billed at off-peak prices all day. MiMo series uses a flat rate with no peak/off-peak distinction.",
4918
+ "info.hint": "Estimate: DeepSeek and MiMo models only, each message priced at the official peak/off-peak rate of its own time (peak: weekdays 09:00–12:00, 14:00–18:00).\nv{version}",
4893
4919
  "badge.aria": "DeepSeek balance {amount}",
4894
4920
  "panel.aria": "DeepSeek balance details",
4895
4921
  "label.sessionRanking": "Today session spend",
@@ -1,12 +1,17 @@
1
1
  import type { DeepSeekSessionSpend, DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from '@rayadesu/dsh-llm-billing/types';
2
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
2
3
  import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
4
  import { NS } from './locales.ts';
4
- /** How many per-session ranking rows the panel shows before the overflow hint. */
5
+ /**
6
+ * How many per-session ranking rows the panel shows before the overflow hint.
7
+ */
5
8
  export declare const SESSION_RANKING_LIMIT = 10;
6
9
  /** Panel props: precomputed amount plus the same spend values the badge holds. */
7
10
  export interface BalancePanelProps {
8
11
  /** Primary balance line, e.g. `¥123.45`; `—` when the provider reports none. */
9
12
  amount: string;
13
+ /** The session this panel belongs to: the row looked up in today's ranking. */
14
+ sessionId: SessionId;
10
15
  spend: DeepSeekSessionSpend | null;
11
16
  todaySpend: DeepSeekTodaySpend | null;
12
17
  sessionsSpend: DeepSeekTodaySessionsSpend | null;
@@ -15,5 +20,5 @@ export interface BalancePanelProps {
15
20
  t: PropsLocale<typeof NS>['t'];
16
21
  }
17
22
  /** The detail box opened from the badge trigger. */
18
- export declare function BalancePanel({ amount, spend, todaySpend, sessionsSpend, refreshing, onRefresh, t }: BalancePanelProps): import("react").JSX.Element;
23
+ export declare function BalancePanel({ amount, sessionId, spend, todaySpend, sessionsSpend, refreshing, onRefresh, t }: BalancePanelProps): import("react").JSX.Element;
19
24
  //# sourceMappingURL=BalancePanel.d.ts.map
@@ -7,7 +7,8 @@ export declare const zh: {
7
7
  readonly 'trigger.conversationSpend': "本轮对话花费:{amount}";
8
8
  readonly 'label.amount': "API 剩余金额:{amount}";
9
9
  readonly 'label.sessionSpend': "本会话花费:{amount}";
10
- readonly 'label.todaySpend': "今日共花费:{amount}";
10
+ readonly 'label.sessionSpend.today': "{amount}";
11
+ readonly 'label.todaySpend': "今日:{amount}";
11
12
  readonly 'label.cost.hit': "缓存命中 {amount}";
12
13
  readonly 'label.cost.input': "未命中输入 {amount}";
13
14
  readonly 'label.cost.output': "输出 {amount}";
@@ -16,7 +17,7 @@ export declare const zh: {
16
17
  readonly 'state.unavailable': "额度不可用";
17
18
  readonly 'action.refresh': "刷新";
18
19
  readonly 'info.aria': "花费说明";
19
- readonly 'info.hint': "仅能预估 DeepSeek MiMo 相关模型。本会话花费按每条消息的发生时刻(北京时间)所在峰谷时段单价计费:缓存命中输入、未命中输入(含缓存写入)、输出(含推理)分别计价。基础费率按 DeepSeek 8 月 17 日实行的标准,其中 flash 系列(V4.1 Flash / V4 Flash / Vision Exp)自 9 月 10 日 12:00 起执行新价(谷时 0.02 / 1.0 / 4.0 元每百万 token,峰时为其两倍),该时刻之前的消息仍按原价;V4 Pro 自 9 月 14 日 12:00 起改由 V4.1 Flash 服务并按其实施费率计费。高峰时段为北京时间周一至周五 9:00–12:00、14:00–18:00,周末全天按低谷价计费;MiMo 系列不区分峰谷,统一费率。";
20
+ readonly 'info.hint': "估算:仅 DeepSeek MiMo 模型,按每条消息自身时刻的峰谷官方单价计价(高峰:工作日 9:00–12:00、14:00–18:00)。\nv{version}";
20
21
  readonly 'badge.aria': "DeepSeek 额度:{amount}";
21
22
  readonly 'panel.aria': "DeepSeek 额度详情";
22
23
  readonly 'label.sessionRanking': "今日会话花费";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The plugin's own version, reported in the spend-hint bubble so a report can
3
+ * name the build actually in use.
4
+ *
5
+ * The value is stamped in at build time rather than read at runtime: a browser
6
+ * bundle has no manifest to read. `packages/tsdown.client.ts` defines
7
+ * `__DSH_PLUGIN_VERSION__` from this package's own `package.json` (the three
8
+ * published packages share one version), and `vitest.config.ts` defines the
9
+ * same constant from the same manifest so the suites see what a release ships.
10
+ * The `typeof` guard keeps a source run without either definition (a foreign
11
+ * bundler, a bare `tsc` check) from throwing on the undeclared global.
12
+ * @module @rayadesu/dsh-client-ui-billing/version
13
+ */
14
+ /** The installed plugin version, e.g. `0.3.10`; `dev` when nothing stamped one. */
15
+ export declare const PLUGIN_VERSION: string;
16
+ //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rayadesu/dsh-client-ui-billing",
3
3
  "description": "Session-header DeepSeek account-balance and conversation-spend badge over the billing Remote",
4
- "version": "0.3.10",
4
+ "version": "0.3.11",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  "access": "public"
48
48
  },
49
49
  "peerDependencies": {
50
- "@rayadesu/dsh-llm-billing": "^0.3.10",
50
+ "@rayadesu/dsh-llm-billing": "^0.3.11",
51
51
  "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.5",
52
52
  "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.5",
53
53
  "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.5",
@@ -58,7 +58,7 @@
58
58
  "@deepseek-ai/dsh-session": "^0.1.2-alpha.5"
59
59
  },
60
60
  "devDependencies": {
61
- "@rayadesu/dsh-llm-billing": "^0.3.10",
61
+ "@rayadesu/dsh-llm-billing": "^0.3.11",
62
62
  "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.5",
63
63
  "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.5",
64
64
  "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.5",