Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions dashboard/src/lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ export function fmtUsd(n: number | null | undefined): string {
return `$${n.toFixed(n < 0.01 ? 6 : 4)}`;
}

/** Keep in sync with src/cli/ui/theme/tokens.ts USD_TO_CNY. */
const USD_TO_CNY = 7.2;

/** USD-internal cost rendered in the wallet's display currency. Undefined currency → CNY (matches CLI default). */
export function fmtCost(
usd: number | null | undefined,
currency: string | null | undefined,
fractionDigits?: number,
): string {
if (usd === null || usd === undefined) return "—";
const cur = currency ?? "CNY";
const amount = cur === "CNY" ? usd * USD_TO_CNY : usd;
if (amount === 0) return cur === "CNY" ? "¥0" : "$0";
const sym = cur === "CNY" ? "¥" : cur === "USD" ? "$" : `${cur} `;
const digits = fractionDigits ?? (Math.abs(amount) < 0.01 ? 6 : 4);
return `${sym}${amount.toFixed(digits)}`;
}

export function fmtPct(n: number | null | undefined): string {
if (n === null || n === undefined) return "—";
return `${(n * 100).toFixed(1)}%`;
Expand Down
11 changes: 6 additions & 5 deletions dashboard/src/panels/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from "../components/chat-internals.js";
import { MODE, TOKEN, api } from "../lib/api.js";
import { appBus, showToast } from "../lib/bus.js";
import { fmtUsd } from "../lib/format.js";
import { fmtCost, fmtUsd } from "../lib/format.js";
import { html } from "../lib/html.js";
import { t, useLang } from "../i18n/index.js";

Expand Down Expand Up @@ -791,6 +791,7 @@ function SideRail({ stats, budgetUsd, activePlan }: SideRailProps) {
const showBudget = stats != null && typeof budgetUsd === "number" && budgetUsd > 0;
const budgetPct = showBudget ? Math.min(120, (stats.totalCostUsd / budgetUsd) * 100) : 0;
const budgetTone = budgetPct >= 100 ? "err" : budgetPct >= 80 ? "warn" : "";
const walletCurrency = stats?.balance?.[0]?.currency;
return html`
<aside class="chat-rail">
${activePlan ? html`<${ActivePlanCard} plan=${activePlan} />` : null}
Expand All @@ -801,7 +802,7 @@ function SideRail({ stats, budgetUsd, activePlan }: SideRailProps) {
<div class="rh">${t("chat.railSession")}</div>
<div class="rail-kv"><span class="k">${t("chat.railTurns")}</span><span class="v">${stats.turns.toLocaleString()}</span></div>
<div class="rail-kv"><span class="k">${t("chat.railPromptTok")}</span><span class="v">${stats.lastPromptTokens.toLocaleString()}</span></div>
<div class="rail-kv"><span class="k">${t("chat.railCost")}</span><span class="v">${fmtUsd(stats.totalCostUsd)}</span></div>
<div class="rail-kv"><span class="k">${t("chat.railCost")}</span><span class="v">${fmtCost(stats.totalCostUsd, walletCurrency)}</span></div>
<div class="progress-row" style="margin-top:8px">
<span class="lbl">${t("chat.railCacheHit")}</span>
<div class=${`progress ${cacheTone}`}><div class="progress-fill" style=${`width:${cachePct}%`}></div></div>
Expand All @@ -819,7 +820,7 @@ function SideRail({ stats, budgetUsd, activePlan }: SideRailProps) {
<div class="progress-row">
<span class="lbl">${t("chat.railSpend")}</span>
<div class=${`progress ${budgetTone}`}><div class="progress-fill" style=${`width:${Math.min(100, budgetPct)}%`}></div></div>
<span class="v" style=${budgetTone === "err" ? "color:var(--c-err)" : budgetTone === "warn" ? "color:var(--c-warn)" : ""}>${fmtUsd(stats.totalCostUsd)} / ${fmtUsd(budgetUsd)}</span>
<span class="v" style=${budgetTone === "err" ? "color:var(--c-err)" : budgetTone === "warn" ? "color:var(--c-warn)" : ""}>${fmtCost(stats.totalCostUsd, walletCurrency)} / ${fmtCost(budgetUsd, walletCurrency)}</span>
</div>
</div>
`
Expand Down Expand Up @@ -989,11 +990,11 @@ function ChatStatusBar({ stats, model }: ChatStatusBarProps) {
</span>
<span class="status-item">
<span class="status-label">${t("chat.statusTurn")}</span>
<code>${fmtUsd(stats.lastTurnCostUsd)}</code>
<code>${fmtCost(stats.lastTurnCostUsd, balance?.currency)}</code>
</span>
<span class="status-item">
<span class="status-label">${t("chat.statusSession")}</span>
<code>${fmtUsd(stats.totalCostUsd)}</code>
<code>${fmtCost(stats.totalCostUsd, balance?.currency)}</code>
<span class="muted" style="font-size: 10px;">
${t("chat.statusTurns", { count: stats.turns, s: stats.turns === 1 ? "" : "s" })}
</span>
Expand Down
7 changes: 4 additions & 3 deletions dashboard/src/panels/overview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fmtCompactNum, fmtNum, fmtRelativeTime, fmtUsd } from "../lib/format.js";
import { fmtCompactNum, fmtCost, fmtNum, fmtRelativeTime, fmtUsd } from "../lib/format.js";
import { html } from "../lib/html.js";
import { usePoll } from "../lib/use-poll.js";
import { t, useLang } from "../i18n/index.js";
Expand Down Expand Up @@ -144,6 +144,7 @@ function currentSessionBlock(c: CockpitData) {
`;
}
const s = c.currentSession;
const currency = c.balance?.currency;
return html`
<div class="cock-list cock-w-2">
<div class="ch"><span class="ttl">${t("overview.currentSession")}</span></div>
Expand All @@ -153,7 +154,7 @@ function currentSessionBlock(c: CockpitData) {
<div style="display:grid;grid-template-columns:repeat(3, 1fr);gap:8px;font-family:var(--font-mono);font-size:11px">
<div><span style="color:var(--fg-3)">${t("overview.promptTok")}</span><div style="color:var(--fg-0);font-size:13px;font-weight:600">${fmtNum(s.lastPromptTokens)}</div></div>
<div><span style="color:var(--fg-3)">${t("overview.completionTok")}</span><div style="color:var(--fg-0);font-size:13px;font-weight:600">${fmtNum(s.completionTokens)}</div></div>
<div><span style="color:var(--fg-3)">${t("overview.cost")}</span><div style="color:var(--fg-0);font-size:13px;font-weight:600">${fmtUsd(s.totalCostUsd)}</div></div>
<div><span style="color:var(--fg-3)">${t("overview.cost")}</span><div style="color:var(--fg-0);font-size:13px;font-weight:600">${fmtCost(s.totalCostUsd, currency)}</div></div>
</div>
</div>
`;
Expand Down Expand Up @@ -185,7 +186,7 @@ function costTrendSpark(c: CockpitData) {
return html`
<div class="chart cock-w-2">
<div class="chart-h"><span class="title">${t("overview.costTrend")}</span></div>
<div class="chart-v">${fmtUsd(avg)}<span class="unit">${t("overview.dayAvg")}</span></div>
<div class="chart-v">${fmtCost(avg, c.balance?.currency)}<span class="unit">${t("overview.dayAvg")}</span></div>
<div class="chart-spark">
<svg viewBox=${`0 0 ${w} ${h}`} preserveAspectRatio="none">
<polyline fill="none" stroke="var(--c-brand)" stroke-width="1.5" points=${points} />
Expand Down
Loading