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

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.
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Session-header billing badge: balance plus the current conversation's billed
3
- * spend. Composition root: the data lifecycle lives in {@link useBillingData},
4
- * and the trigger / detail panel are pure views. The badge renders null until
5
- * the first balance fetch settles, and a refresh keeps the last values
6
- * visible rather than blanking them.
3
+ * spend, both carrying the detail panel's own labels (`API 剩余金额` /
4
+ * `本会话花费`). Composition root: the data lifecycle lives in
5
+ * {@link useBillingData}, and the trigger / detail panel are pure views. The
6
+ * badge renders null until the first balance fetch settles, and a refresh keeps
7
+ * the last values visible rather than blanking them.
7
8
  */
8
- import type { DeepSeekBalance, DeepSeekSessionSpend, DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from '@rayadesu/dsh-llm-billing/types';
9
+ import type { DeepSeekBalance, DeepSeekDelegatedSpend, DeepSeekSessionSpend, DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from '@rayadesu/dsh-llm-billing/types';
9
10
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
10
11
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
12
  import { NS } from './locales.ts';
@@ -28,6 +29,13 @@ export interface BalanceBadgeInjected {
28
29
  getCachedBalance: () => DeepSeekBalance | null;
29
30
  /** Read one session's billed spend; rejects with the Remote error message. */
30
31
  getSessionSpend: (sessionId: SessionId) => Promise<DeepSeekSessionSpend>;
32
+ /**
33
+ * Read the subagent part of one conversation's billed spend: every subagent
34
+ * session that session delegated, transitively, across every day. The badge
35
+ * adds it to the live own-session value, so the amount it shows is the whole
36
+ * conversation's. `force` behaves as in {@link getTodaySpend}.
37
+ */
38
+ getDelegatedSpend: (sessionId: SessionId, force?: boolean) => Promise<DeepSeekDelegatedSpend>;
31
39
  /**
32
40
  * Read today's billed spend across every session; rejects with the Remote
33
41
  * error message. `force` bypasses the host-side cache — the manual refresh
@@ -36,7 +44,9 @@ export interface BalanceBadgeInjected {
36
44
  getTodaySpend: (force?: boolean) => Promise<DeepSeekTodaySpend>;
37
45
  /**
38
46
  * Read today's billed spend per session, sorted by cost descending; rejects
39
- * with the Remote error message. `force` behaves as in {@link getTodaySpend}.
47
+ * with the Remote error message. One row per top-level conversation: the host
48
+ * merges every subagent session's spend into the row of the session that
49
+ * delegated it. `force` behaves as in {@link getTodaySpend}.
40
50
  */
41
51
  getTodaySessionsSpend: (force?: boolean) => Promise<DeepSeekTodaySessionsSpend>;
42
52
  }
@@ -50,5 +60,5 @@ export type BalanceBadgeProps = PropsRuntime<'conversation.session.header.utilit
50
60
  * @param props - Remote face, locale, and the standard session-header runtime share.
51
61
  * @returns the badge, or null until the first balance fetch settles.
52
62
  */
53
- export declare function BalanceBadge({ getBalance, getCachedBalance, getSessionSpend, getTodaySpend, getTodaySessionsSpend, sessionId, useSession, useProjection, t }: BalanceBadgeProps): import("react").JSX.Element | null;
63
+ export declare function BalanceBadge({ getBalance, getCachedBalance, getSessionSpend, getTodaySpend, getTodaySessionsSpend, getDelegatedSpend, sessionId, useSession, useProjection, t }: BalanceBadgeProps): import("react").JSX.Element | null;
54
64
  //# sourceMappingURL=BalanceBadge.d.ts.map
@@ -12,13 +12,18 @@ export interface BalancePanelProps {
12
12
  amount: string;
13
13
  /** The session this panel belongs to: the row looked up in today's ranking. */
14
14
  sessionId: SessionId;
15
+ /** The WHOLE conversation's billed spend (this session plus the subagents it delegated). */
15
16
  spend: DeepSeekSessionSpend | null;
16
17
  todaySpend: DeepSeekTodaySpend | null;
17
18
  sessionsSpend: DeepSeekTodaySessionsSpend | null;
19
+ /** Whether this session is itself a delegated subagent child (so it has no ranking row of its own). */
20
+ isSubagent: boolean;
21
+ /** Whether this session started on an earlier Beijing day (the only time a today share is shown). */
22
+ crossedDay: boolean;
18
23
  refreshing: boolean;
19
24
  onRefresh: () => void;
20
25
  t: PropsLocale<typeof NS>['t'];
21
26
  }
22
27
  /** The detail box opened from the badge trigger. */
23
- export declare function BalancePanel({ amount, sessionId, spend, todaySpend, sessionsSpend, refreshing, onRefresh, t }: BalancePanelProps): import("react").JSX.Element;
28
+ export declare function BalancePanel({ amount, sessionId, spend, todaySpend, sessionsSpend, isSubagent, crossedDay, refreshing, onRefresh, t }: BalancePanelProps): import("react").JSX.Element;
24
29
  //# sourceMappingURL=BalancePanel.d.ts.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Composer spend card: the money-bag pill under the input box that opens this
3
+ * conversation's billed-cost card. The card reproduces DSH's own token-usage
4
+ * dialog skin row for row (same surface, radius, elevation, title rule, and
5
+ * right-aligned tabular amounts) with the spend's three billing buckets where
6
+ * that card shows its token buckets: uncached input, cache read, output.
7
+ *
8
+ * The amounts ride the host-pushed `billingTodaySpend` projection, so the card
9
+ * is live with zero Remote calls; `billing/getSessionSpend` is the fallback for
10
+ * an assembly whose projection registry is absent (the same ladder the header
11
+ * badge walks in `useBillingData`).
12
+ * @module @rayadesu/dsh-client-ui-billing/SpendCard
13
+ */
14
+ import type { DeepSeekSessionSpend } from '@rayadesu/dsh-llm-billing/types';
15
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
16
+ import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
17
+ import { NS } from './locales.ts';
18
+ /** Injected face: this conversation's priced spend, for the projection-less fallback. */
19
+ export interface SpendCardInjected {
20
+ /** Read one session's billed spend across its own events (Remote fallback). */
21
+ getSessionSpend: (sessionId: SessionId) => Promise<DeepSeekSessionSpend>;
22
+ }
23
+ /**
24
+ * Full props of the composer spend entry. The owner share arrives from
25
+ * ui-conversation's declared `conversation.composer.dock` list slot (it passes
26
+ * no owner values); the session runtime share adds the identity and the
27
+ * projection reader, as it does for every session-scoped slot.
28
+ */
29
+ export type SpendCardProps = PropsRuntime<'conversation.composer.dock'> & InjectFace<SpendCardInjected> & PropsLocale<typeof NS>;
30
+ /**
31
+ * Render the composer spend pill, and the cost card it opens.
32
+ *
33
+ * The pill mirrors DSH's own composer stat pills (14px glyph, tertiary tone,
34
+ * tabular amount, hover pill) in its own row under the input box, because the
35
+ * composer dock is a list slot whose entries the composer stacks. A session
36
+ * that priced nothing renders no row at all.
37
+ * @param props - the session identity, the projection reader, the Remote fallback, and the locale seat.
38
+ * @returns the pill plus its portaled card, or null when there is nothing to show.
39
+ */
40
+ export declare function SpendCard({ sessionId, useProjection, getSessionSpend, t }: SpendCardProps): import("react").JSX.Element | null;
41
+ //# sourceMappingURL=SpendCard.d.ts.map
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Shared display formatting for the billing badges: the balance-line currency
3
- * prefix and the CNY spend amount renderer. Both the header badge and the
4
- * per-turn cost label format through these, so the two entries cannot drift
5
- * apart.
3
+ * prefix, the CNY spend amount renderer, the token-count renderer, and the
4
+ * cache-hit percentage. Both the header badge and the per-turn cost label
5
+ * format through these, so the two entries cannot drift apart.
6
6
  */
7
7
  import type { DeepSeekBalance } from '@rayadesu/dsh-llm-billing/types';
8
8
  /** Currency prefix for one balance line; unknown codes render as a literal prefix. */
@@ -12,6 +12,83 @@ export declare function primaryLine(balance: DeepSeekBalance): {
12
12
  symbol: string;
13
13
  total: string;
14
14
  } | undefined;
15
- /** CNY amount, up to four decimals with trailing zeros trimmed. */
15
+ /**
16
+ * CNY amount, up to four decimals with trailing zeros trimmed. The balance line's
17
+ * renderer; spend figures go through {@link formatSpendSignificant} instead.
18
+ */
16
19
  export declare function formatSpend(amount: number): string;
20
+ /**
21
+ * CNY spend amount at a fixed number of SIGNIFICANT digits (default three),
22
+ * trailing zeros trimmed, prefixed with `¥` — and never finer than the panel's
23
+ * own four-decimal resolution.
24
+ *
25
+ * Every spend figure the plugin renders goes through this: the day's own amount
26
+ * and its three bucket costs, this session's spend and the parenthesized today
27
+ * share after it, the per-model rows and their bucket breakdowns, the ranking
28
+ * amounts, and the badge's own spend line. Four decimals are noise at these
29
+ * magnitudes — their last digits move with every request (`¥0.5068` → `¥0.507`)
30
+ * — while the four-decimal bound keeps a microscopic amount from growing a long
31
+ * `¥0.00002` string that would widen its column: an amount that rounds to zero
32
+ * there reads `¥0`. Only the BALANCE line keeps {@link formatSpend}'s plain four
33
+ * decimals, because remaining credit is a balance, not a spend measurement.
34
+ * @param amount - the amount to render.
35
+ * @param digits - significant digits to keep (default 3).
36
+ * @returns the rendered amount, e.g. `¥9.58`, `¥0.507`, `¥1230`, `¥0`.
37
+ */
38
+ export declare function formatSpendSignificant(amount: number, digits?: number): string;
39
+ /**
40
+ * A token count in DSH's own compact notation: `517` / `12.2K` / `517K` / `1.2M`.
41
+ *
42
+ * Mirrors ui-chat's `formatTokens` (`packages/client/ui-chat/src/client/chat/
43
+ * token-format.ts`) rule for rule, so one shell never renders the same count two
44
+ * ways. That rule reads: below 1e3 the plain integer (no digit grouping); below
45
+ * 1e6 the shared `number.thousand` unit (`{value}K`); otherwise the shared
46
+ * `number.million` unit (`{value}M`). A scaled value keeps one decimal while it
47
+ * is below 100 and rounds to a whole number from 100 up (`12.2K`, but `517K`),
48
+ * and there is no third unit, so 1.2e9 renders as `1200M` — exactly as DSH
49
+ * renders it. The unit letters are identical in both DSH dictionaries, so they
50
+ * are literals here rather than a locale lookup into the shared vocabulary.
51
+ * @param count - the token count to render.
52
+ * @returns the compact count, without the label's own ` tok` suffix.
53
+ */
54
+ export declare function formatTokens(count: number): string;
55
+ /**
56
+ * The cache-hit share of one prompt, in DSH's own percentage rule.
57
+ *
58
+ * Mirrors ui-chat's `formatCacheHitPercent`
59
+ * (`packages/client/ui-chat/src/client/chat/token-format.ts`) rule for rule, so
60
+ * one shell never prints the same ratio two ways: an integer percentage by
61
+ * default, and — because a partial hit must never round up to a full one — just
62
+ * enough extra decimals to stay below 100 when integer rounding would reach it
63
+ * (`99`, `99.5`, `99.95`, …). The rounding runs in integer arithmetic with
64
+ * positive ties going up, so it does not ride on binary-float precision.
65
+ *
66
+ * The ratio is over PROMPT-side tokens only (cache read over cache read plus
67
+ * uncached input, the split DSH's own `billedInputTokens` makes); output tokens
68
+ * never enter the denominator.
69
+ * @param cacheReadTokens - exact prompt tokens served from cache.
70
+ * @param promptTokens - exact aggregate prompt-side tokens.
71
+ * @param decimalPlaces - ordinary-ratio precision; a partial hit that would
72
+ * round to 100 automatically uses enough additional precision to stay honest.
73
+ * @returns percentage text without its `%`, or null when there was no prompt input.
74
+ */
75
+ export declare function formatCacheHitPercent(cacheReadTokens: number, promptTokens: number, decimalPlaces?: 0 | 1): string | null;
76
+ /** Every billed token bucket of one priced model row. */
77
+ export interface TokenBuckets {
78
+ /** Cache-hit input tokens. */
79
+ cacheHitInputTokens: number;
80
+ /** Cache-miss input tokens (uncached input plus cache writes). */
81
+ cacheMissInputTokens: number;
82
+ /** Output tokens (reasoning included). */
83
+ outputTokens: number;
84
+ }
85
+ /**
86
+ * The total billed tokens of a priced row: the three buckets summed. Tokens are
87
+ * already counted per bucket when they were priced, so a cache-hit input token
88
+ * and an output token are both one token here — the number answers "how many
89
+ * tokens did this cost", not "how many characters were sent".
90
+ * @param row - one priced model row.
91
+ * @returns the row's total token count.
92
+ */
93
+ export declare function rowTokens(row: TokenBuckets): number;
17
94
  //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * In-house glyphs shared by this plugin's entries. Today's only member is the
3
+ * money bag the composer spend card and the per-turn cost label both lead with,
4
+ * so the two spend surfaces cannot drift apart iconographically.
5
+ * @module @rayadesu/dsh-client-ui-billing/icons
6
+ */
7
+ /**
8
+ * In-house money-bag glyph in the hollow-outline style (16px viewBox, stroke
9
+ * currentColor, fill none), referencing the 💰 emoji: a bag outline with a
10
+ * tie knot at the neck and a centered yuan mark on the body. The drawing
11
+ * fills the 16px box like the other glyphs; a host that renders it at 14px
12
+ * (the DSH glyph tier) keeps the same geometry and only scales down.
13
+ * @param props - optional size and class.
14
+ * @returns the money-bag icon.
15
+ */
16
+ export declare function WalletIcon({ size, className }: {
17
+ size?: number;
18
+ className?: string;
19
+ }): import("react").JSX.Element;
20
+ //# sourceMappingURL=icons.d.ts.map
@@ -2,6 +2,7 @@
2
2
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
3
3
  import { type BillingKey } from './locales.ts';
4
4
  export type { BalanceBadgeInjected, BalanceBadgeProps } from './BalanceBadge.tsx';
5
+ export type { SpendCardInjected, SpendCardProps } from './SpendCard.tsx';
5
6
  export type { TurnCostActionInjected, TurnCostActionProps } from './TurnCostAction.tsx';
6
7
  export { createTurnCostStore, TURN_COST_STORE_LIMIT, type TurnCostStore } from './turnCostStore.ts';
7
8
  export type { BillingKey } from './locales.ts';
@@ -3,21 +3,28 @@
3
3
  export declare const NS = "billing";
4
4
  /** Simplified Chinese dictionary (the key-set source of truth). */
5
5
  export declare const zh: {
6
- readonly 'trigger.balance': "剩余额度:{amount}";
7
- readonly 'trigger.conversationSpend': "本轮对话花费:{amount}";
6
+ readonly 'trigger.balance': "剩余金额:{amount}";
8
7
  readonly 'label.amount': "API 剩余金额:{amount}";
9
8
  readonly 'label.sessionSpend': "本会话花费:{amount}";
10
- readonly 'label.sessionSpend.today': "{amount}";
11
- readonly 'label.todaySpend': "今日:{amount}";
12
- readonly 'label.cost.hit': "缓存命中 {amount}";
13
- readonly 'label.cost.input': "未命中输入 {amount}";
9
+ readonly 'label.sessionSpend.today': " {amount}";
10
+ readonly 'label.todaySpend': "今日花费:{amount}";
11
+ readonly 'label.todayTokens': "今日 Token:{count}";
12
+ readonly 'label.todayTokens.hit': " {percent}%";
13
+ readonly 'unit.tokens': "{count} tok";
14
+ readonly 'label.cost.input': "未缓存输入 {amount}";
15
+ readonly 'label.cost.cacheRead': "缓存读取 {amount}";
14
16
  readonly 'label.cost.output': "输出 {amount}";
17
+ readonly 'card.title': "花费金额";
18
+ readonly 'card.aria': "本轮对话花费:{amount}";
19
+ readonly 'label.bucket.input': "未缓存输入";
20
+ readonly 'label.bucket.cacheRead': "缓存读取";
21
+ readonly 'label.bucket.output': "输出";
15
22
  readonly 'stat.none': "暂无消耗记录";
16
23
  readonly 'stat.untitled': "未命名";
17
24
  readonly 'state.unavailable': "额度不可用";
18
25
  readonly 'action.refresh': "刷新";
19
26
  readonly 'info.aria': "花费说明";
20
- readonly 'info.hint': "估算:仅 DeepSeek 与 MiMo 模型,按每条消息自身时刻的峰谷官方单价计价(高峰:工作日 9:00–12:00、14:00–18:00)。\nv{version}";
27
+ readonly 'info.hint': "估算:仅 DeepSeek 与 MiMo 模型,按每条消息自身时刻的峰谷官方单价计价(高峰:工作日 9:00–12:00、14:00–18:00)。\n金额含本会话委派的子代理会话。\n紧跟的数字为本会话今日花费。\nv{version}";
21
28
  readonly 'badge.aria': "DeepSeek 额度:{amount}";
22
29
  readonly 'panel.aria': "DeepSeek 额度详情";
23
30
  readonly 'label.sessionRanking': "今日会话花费";
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The composer spend card's three bucket amounts, derived from the pushed
3
+ * `billingTodaySpend` projection. The host fold already prices every sample
4
+ * into the three disjoint billing buckets (cache-hit input, cache-miss input —
5
+ * which includes cache writes, output including reasoning), so the card sums
6
+ * the session's priced model rows instead of adding a Remote call or a second
7
+ * fold. Keeping the derivation here (pure, no React) lets the sum and its
8
+ * float reconciliation be tested on their own.
9
+ * @module @rayadesu/dsh-client-ui-billing/spendBuckets
10
+ */
11
+ import type { DeepSeekTodaySpend } from '@rayadesu/dsh-llm-billing/types';
12
+ /** One priced row's cost split across the three billing buckets, in CNY. */
13
+ export interface SpendBuckets {
14
+ /** Uncached (cache-miss) input cost, cache writes included. */
15
+ uncachedInput: number;
16
+ /** Cache-hit (cache-read) input cost. */
17
+ cacheRead: number;
18
+ /** Output cost, reasoning included. */
19
+ output: number;
20
+ /** Billed total the three rows must add up to. */
21
+ total: number;
22
+ }
23
+ /**
24
+ * Fold one spend's model rows into the card's three bucket amounts.
25
+ *
26
+ * The rows carry float costs, so the three independently rendered values can
27
+ * miss the displayed total by one unit in the last rendered decimal after each
28
+ * is formatted to four decimals. The residual is absorbed by the largest
29
+ * bucket — the only one where a 1e-4 nudge cannot be seen — so the card's rows
30
+ * always add up to the total it shows in its header.
31
+ * @param spend - the session bucket of the `billingTodaySpend` projection
32
+ * (the session's own events, every Beijing day).
33
+ * @returns the three bucket totals plus the billed total.
34
+ */
35
+ export declare function spendBucketsOf(spend: DeepSeekTodaySpend): SpendBuckets;
36
+ /** Whether a spend has any priced amount to show (a zero card stays hidden). */
37
+ export declare function hasBilledSpend(spend: DeepSeekTodaySpend): boolean;
38
+ /** One spend's three bucket TOKEN counts, in DSH's row order. */
39
+ export interface TokenBucketCounts {
40
+ /** Uncached (cache-miss) input tokens, cache writes included. */
41
+ uncachedInput: number;
42
+ /** Cache-hit (cache-read) input tokens. */
43
+ cacheRead: number;
44
+ /** Output tokens, reasoning included. */
45
+ output: number;
46
+ }
47
+ /**
48
+ * Fold one spend's model rows into the three bucket TOKEN counts — the token
49
+ * side of {@link spendBucketsOf}, so a surface that shows both (the panel's
50
+ * today rows) reads one bucket's tokens and its cost from the same rows.
51
+ *
52
+ * No reconciliation is needed here: tokens are integers, so the three counts
53
+ * always add up to the total the token line shows.
54
+ * @param spend - the day's (or one session's) priced rows.
55
+ * @returns the three bucket token counts.
56
+ */
57
+ export declare function tokenBucketsOf(spend: DeepSeekTodaySpend): TokenBucketCounts;
58
+ /**
59
+ * The cache-hit share of one spend, in DSH's own percentage rule — the ratio of
60
+ * the cache-read bucket to every PROMPT-side bucket (uncached input with cache
61
+ * writes folded in, plus cache read), output excluded, exactly the split DSH's
62
+ * `billedInputTokens` makes. Rendered by {@link formatCacheHitPercent}, i.e. an
63
+ * integer percent that grows decimals only to keep a partial hit below 100.
64
+ * @param spend - the day's priced rows.
65
+ * @returns the percentage text without its `%`, or null when the spend billed no prompt input.
66
+ */
67
+ export declare function cacheHitPercentOf(spend: DeepSeekTodaySpend): string | null;
68
+ //# sourceMappingURL=spendBuckets.d.ts.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Additive merge of a conversation's two spend halves: the session's OWN billed
3
+ * spend (live, from the pushed `billingTodaySpend` projection or the
4
+ * `billing/getSessionSpend` fallback) and the delegated-subagent subtotal
5
+ * (`billing/getDelegatedSpend`). The panel shows one amount for the whole
6
+ * conversation, so the two must be summed before rendering — and the per-model
7
+ * rows must be summed the same way, or the breakdown would no longer add up to
8
+ * the amount above it.
9
+ *
10
+ * Pure addition only: no pricing, no timezone, no fork-boundary knowledge lives
11
+ * here (the host owns all of that), so this cannot drift from the host's own
12
+ * sums.
13
+ */
14
+ import type { DeepSeekSessionSpend } from '@rayadesu/dsh-llm-billing/types';
15
+ /**
16
+ * Sum one conversation's own spend and its delegated-subagent subtotal.
17
+ * @param own - the session's own billed spend.
18
+ * @param delegated - the delegated subtotal from the last `getDelegatedSpend` read.
19
+ * @returns the merged spend (own model order first, then newly seen models).
20
+ */
21
+ export declare function sumSpends(own: DeepSeekSessionSpend, delegated: DeepSeekSessionSpend): DeepSeekSessionSpend;
22
+ //# sourceMappingURL=spends.d.ts.map
@@ -6,9 +6,24 @@ export declare const TURN_SETTLE_DEBOUNCE_MS = 2000;
6
6
  /** The data surface the trigger and the panel render from. */
7
7
  export interface BillingData {
8
8
  balance: DeepSeekBalance | null;
9
+ /**
10
+ * The WHOLE conversation's billed spend: this session's own spend (live, from
11
+ * the pushed projection or the `getSessionSpend` fallback) plus the subagent
12
+ * sessions it delegated (the last `getDelegatedSpend` read), so the amount a
13
+ * user reads is the conversation's, not just its own log's.
14
+ */
9
15
  spend: DeepSeekSessionSpend | null;
10
16
  todaySpend: DeepSeekTodaySpend | null;
11
17
  sessionsSpend: DeepSeekTodaySessionsSpend | null;
18
+ /** Whether the current session is itself a delegated subagent child. */
19
+ isSubagent: boolean;
20
+ /**
21
+ * Whether the current session started on an EARLIER Beijing day — the only
22
+ * case in which the panel's parenthesized today share is meaningful.
23
+ * `false` until the delegated read settles (an unproven crossing stays
24
+ * hidden) and for every session created today.
25
+ */
26
+ crossedDay: boolean;
12
27
  /** Balance fetch failure while no value is present yet. */
13
28
  error: string | null;
14
29
  refreshing: boolean;
@@ -19,12 +34,14 @@ export interface BillingData {
19
34
  }
20
35
  /**
21
36
  * Start the badge's data lifecycle for one session. The spend follows the
22
- * conversation: the host-pushed `billingTodaySpend` projection drives the
23
- * session line live (zero Remote calls), with `getSessionSpend` as the
24
- * bootstrap/fallback when the projection key is absent; today's spend is
25
- * recomputed through `getTodaySpend` when a turn settles; the balance stays a
26
- * manual-refresh snapshot and is never refetched on its own.
37
+ * conversation: the host-pushed `billingTodaySpend` projection drives this
38
+ * session's own part live (zero Remote calls), with `getSessionSpend` as the
39
+ * bootstrap/fallback when the projection key is absent; the delegated-subagent
40
+ * subtotal comes from `getDelegatedSpend` on mount, on refresh, and when a turn
41
+ * settles; today's spend is recomputed through `getTodaySpend` on the same
42
+ * events; the balance stays a manual-refresh snapshot and is never refetched on
43
+ * its own.
27
44
  * @param props - the badge's injected face and session runtime share.
28
45
  */
29
- export declare function useBillingData({ getBalance, getCachedBalance, getSessionSpend, getTodaySpend, getTodaySessionsSpend, sessionId, useSession, useProjection, }: Pick<BalanceBadgeProps, 'getBalance' | 'getCachedBalance' | 'getSessionSpend' | 'getTodaySpend' | 'getTodaySessionsSpend' | 'sessionId' | 'useSession' | 'useProjection'>): BillingData;
46
+ export declare function useBillingData({ getBalance, getCachedBalance, getSessionSpend, getTodaySpend, getTodaySessionsSpend, getDelegatedSpend, sessionId, useSession, useProjection, }: Pick<BalanceBadgeProps, 'getBalance' | 'getCachedBalance' | 'getSessionSpend' | 'getTodaySpend' | 'getTodaySessionsSpend' | 'getDelegatedSpend' | 'sessionId' | 'useSession' | 'useProjection'>): BillingData;
30
47
  //# sourceMappingURL=useBillingData.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * One trigger-anchored dialog seat for the composer spend card, mirroring the
3
+ * behaviour of DSH's own composer stat dialogs: the panel portals above the
4
+ * trigger, the position is clamped inside the viewport, an outside pointerdown
5
+ * or Escape closes it.
6
+ *
7
+ * DSH's `ui-chat` keeps its equivalent seat in its own private client bundle,
8
+ * so this plugin cannot import it; the two primitives it is built from are
9
+ * public (`@deepseek-ai/dsh-client-ui-primitives`), and the CSS skin they are
10
+ * paired with is reproduced in `SpendCard.module.css` from the same tokens.
11
+ * @module @rayadesu/dsh-client-ui-billing/useCardDialog
12
+ */
13
+ import { type CSSProperties, type MutableRefObject } from 'react';
14
+ /**
15
+ * Unplaced portal panel: hidden but laid out, so the clamp's measure pass sees
16
+ * the panel's real dimensions instead of clamping a zero-size box.
17
+ */
18
+ export declare const MEASURE_STYLE: CSSProperties;
19
+ /** Open state, the two refs, and the clamped placement of one card dialog. */
20
+ export interface CardDialogSeat {
21
+ open: boolean;
22
+ setOpen: (open: boolean) => void;
23
+ /** Anchor: wraps the trigger pill so the clamp measures the pill, not the page. */
24
+ rootRef: MutableRefObject<HTMLSpanElement | null>;
25
+ panelRef: MutableRefObject<HTMLDivElement | null>;
26
+ /** Clamped placement; `null` during the measure pass (use {@link MEASURE_STYLE}). */
27
+ pos: CSSProperties | null;
28
+ }
29
+ /**
30
+ * Own the card's open state, placement, and dismissal.
31
+ * @returns the seat; spread `pos ?? MEASURE_STYLE` onto the portaled panel.
32
+ */
33
+ export declare function useCardDialog(): CardDialogSeat;
34
+ //# sourceMappingURL=useCardDialog.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.11",
4
+ "version": "0.3.13",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -47,38 +47,58 @@
47
47
  "access": "public"
48
48
  },
49
49
  "peerDependencies": {
50
- "@rayadesu/dsh-llm-billing": "^0.3.11",
51
- "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.5",
52
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.5",
53
- "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.5",
54
- "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.5",
55
- "@deepseek-ai/cordis": "^4.0.1",
56
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.5",
57
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.5",
58
- "@deepseek-ai/dsh-session": "^0.1.2-alpha.5"
50
+ "@rayadesu/dsh-llm-billing": "^0.3.13",
51
+ "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.1",
52
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.1",
53
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
54
+ "@deepseek-ai/dsh-invariants": "^0.1.6-alpha.1",
55
+ "@deepseek-ai/cordis": "^4.0.2",
56
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
57
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.1",
58
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.1"
59
59
  },
60
60
  "devDependencies": {
61
- "@rayadesu/dsh-llm-billing": "^0.3.11",
62
- "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.5",
63
- "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.5",
64
- "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.5",
65
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.2-alpha.5",
66
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.5",
67
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.5",
68
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.5",
69
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.5",
70
- "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.5",
71
- "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.5",
61
+ "@rayadesu/dsh-llm-billing": "^0.3.13",
62
+ "@deepseek-ai/dsh-api-gateway": "^0.1.6-alpha.1",
63
+ "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.1",
64
+ "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.1",
65
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.1",
66
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.6-alpha.1",
67
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.1",
68
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.1",
69
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.1",
70
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
71
+ "@deepseek-ai/dsh-invariants": "^0.1.6-alpha.1",
72
72
  "@testing-library/dom": "^10.4.1",
73
73
  "@testing-library/react": "^16.3.2",
74
74
  "@types/react": "~18.3.1",
75
+ "@types/react-dom": "~18.3.0",
75
76
  "react": "^18.2.0",
76
77
  "react-dom": "^18.2.0",
77
78
  "use-sync-external-store": "^1.2.0",
78
- "@deepseek-ai/cordis": "^4.0.1",
79
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.5",
80
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.5",
81
- "@deepseek-ai/dsh-session": "^0.1.2-alpha.5"
79
+ "zustand": "~4.4.7",
80
+ "immer": "^10.1.1",
81
+ "@deepseek-ai/cordis": "^4.0.2",
82
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
83
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.1",
84
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.1",
85
+ "@shikijs/langs": "^4.3.1",
86
+ "anser": "^2.3.5",
87
+ "clsx": "^2.0.0",
88
+ "diff": "^9.0.0",
89
+ "katex": "^0.16.47",
90
+ "mdast-util-from-markdown": "^2.0.3",
91
+ "mdast-util-gfm": "^3.1.0",
92
+ "mdast-util-math": "^3.0.0",
93
+ "micromark-core-commonmark": "^2.0.3",
94
+ "micromark-extension-gfm": "^3.0.0",
95
+ "micromark-extension-math": "^3.1.0",
96
+ "micromark-factory-space": "^2.0.1",
97
+ "micromark-util-character": "^2.1.1",
98
+ "micromark-util-classify-character": "^2.0.1",
99
+ "micromark-util-sanitize-uri": "^2.0.1",
100
+ "micromark-util-symbol": "^2.0.1",
101
+ "shiki": "^4.3.1"
82
102
  },
83
103
  "files": [
84
104
  "lib/index.js",