diff --git a/apps/desktop-tauri/src/components/MenuCard.chartRefresh.test.tsx b/apps/desktop-tauri/src/components/MenuCard.chartRefresh.test.tsx
index 8b36d224bb0..f31725dc693 100644
--- a/apps/desktop-tauri/src/components/MenuCard.chartRefresh.test.tsx
+++ b/apps/desktop-tauri/src/components/MenuCard.chartRefresh.test.tsx
@@ -22,13 +22,15 @@ import { buildBundle } from "../test/localeHarness";
import type { ProviderUsageSnapshot } from "../types/bridge";
import MenuCard from "./MenuCard";
-const window0 = makeRateWindow(10, { reserveWillLastToReset: false, reserveEtaSeconds: null });
+function rateWindow(used: number) {
+ return makeRateWindow(used, { reserveWillLastToReset: false, reserveEtaSeconds: null });
+}
-function snapshot(updatedAt: string): ProviderUsageSnapshot {
+function snapshot(updatedAt: string, used: number): ProviderUsageSnapshot {
return makeUsageSnapshot("claude", {
displayName: "Claude",
- primary: window0,
- selectedMetric: window0,
+ primary: rateWindow(used),
+ selectedMetric: rateWindow(used),
sourceLabel: "oauth",
updatedAt,
errorState: "unknown",
@@ -36,11 +38,11 @@ function snapshot(updatedAt: string): ProviderUsageSnapshot {
});
}
-function card(updatedAt: string) {
+function card(updatedAt: string, used = 10) {
return (
@@ -63,23 +65,29 @@ describe("MenuCard chart refresh", () => {
});
});
- it("re-reads chart data only when the local day changes across an update", async () => {
+ it("re-reads chart data after same-day usage updates and across midnight", async () => {
vi.useFakeTimers({ toFake: ["Date"] });
try {
vi.setSystemTime(new Date(2026, 9, 7, 23, 0, 0));
const { rerender } = render(card("2026-10-07T23:00:00Z"));
await waitFor(() => expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(1));
- // Plain rerender and a same-day provider update do not refetch.
+ // A plain rerender must not start a second scan.
rerender(card("2026-10-07T23:00:00Z"));
- vi.setSystemTime(new Date(2026, 9, 7, 23, 30, 0));
- rerender(card("2026-10-07T23:30:00Z"));
expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(1);
+ // A refresh that only moves the timestamp must not rescan either.
+ vi.setSystemTime(new Date(2026, 9, 7, 23, 15, 0));
+ rerender(card("2026-10-07T23:15:00Z"));
+ expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(1);
+ // Published usage changes must reach the chart on the same day too.
+ vi.setSystemTime(new Date(2026, 9, 7, 23, 30, 0));
+ rerender(card("2026-10-07T23:30:00Z", 12));
+ await waitFor(() => expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(2));
// The next update lands after local midnight; Today must be re-read.
vi.setSystemTime(new Date(2026, 9, 8, 0, 5, 0));
- rerender(card("2026-10-08T00:05:00Z"));
- await waitFor(() => expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(2));
+ rerender(card("2026-10-08T00:05:00Z", 12));
+ await waitFor(() => expect(tauriMocks.getProviderChartData).toHaveBeenCalledTimes(3));
} finally {
vi.useRealTimers();
}
diff --git a/apps/desktop-tauri/src/components/MenuCard.tsx b/apps/desktop-tauri/src/components/MenuCard.tsx
index d8747691763..725a46eb42a 100644
--- a/apps/desktop-tauri/src/components/MenuCard.tsx
+++ b/apps/desktop-tauri/src/components/MenuCard.tsx
@@ -184,10 +184,15 @@ export default function MenuCard({
setChartData(null);
}, [provider.providerId, provider.accountEmail]);
- // Re-read the local-usage chart when the local calendar day changes (seen on
- // the next render, e.g. a provider update), so "Today" does not freeze at the
- // first mount without a full transcript walk on every refresh.
+ // Re-read the local-usage chart after a provider publishes changed usage and
+ // when a render crosses the local calendar day, so Today keeps advancing.
+ // `updatedAt` changes on every refresh, and each read can walk the local
+ // transcripts, so key on the usage values instead.
const localDay = new Date().toLocaleDateString("sv");
+ const usageRevision = [provider.primary, provider.secondary, provider.tertiary]
+ .map((window) => window?.usedPercent ?? "")
+ .concat(provider.cost?.used ?? "")
+ .join("|");
useEffect(() => {
if (!providerSupportsChartData(provider.providerId) || accountScoped) return;
let cancelled = false;
@@ -207,7 +212,7 @@ export default function MenuCard({
if (frame !== undefined) cancelAnimationFrame(frame);
};
// "Track costs" flips local estimates on or off; the backend answers for the new value.
- }, [provider.providerId, provider.accountEmail, localDay, costTrackingEnabled, accountScoped, onLayoutChange]);
+ }, [provider.providerId, provider.accountEmail, usageRevision, localDay, costTrackingEnabled, accountScoped, onLayoutChange]);
// The Conversations list reads the same backend-cached summary as Usage & Spend,
// which masks titles itself; a summary fetched under the other Hide Personal
diff --git a/apps/desktop-tauri/src/components/card/InlineDashboard.test.tsx b/apps/desktop-tauri/src/components/card/InlineDashboard.test.tsx
index 7fdba8534a0..9faaa768c17 100644
--- a/apps/desktop-tauri/src/components/card/InlineDashboard.test.tsx
+++ b/apps/desktop-tauri/src/components/card/InlineDashboard.test.tsx
@@ -12,6 +12,7 @@ vi.mock("../../hooks/useLocale", () => ({
import InlineDashboard, { barHeight, barOpacity } from "./InlineDashboard";
import type { InlineDashboardModel } from "../../lib/inlineDashboard";
+import { inlineDashboardModel } from "../../lib/inlineDashboard";
const model: InlineDashboardModel = {
kpis: [
@@ -19,9 +20,9 @@ const model: InlineDashboardModel = {
{ title: "30d cost", value: "$25.42", emphasis: false },
],
points: [
- { date: "2026-10-09", value: 2, title: "2026-10-09: $2.00 · 10K tokens" },
- { date: "2026-10-10", value: null, title: "2026-10-10" },
- { date: "2026-10-11", value: 8, title: "2026-10-11: $8.00 · 40K tokens" },
+ { date: "2026-10-09", value: 2, hasUnpricedUsage: false, title: "2026-10-09: $2.00 · 10K tokens" },
+ { date: "2026-10-10", value: null, hasUnpricedUsage: false, title: "2026-10-10" },
+ { date: "2026-10-11", value: 8, hasUnpricedUsage: false, title: "2026-10-11: $8.00 · 40K tokens" },
],
maxValue: 8,
scaleLabel: "$8",
@@ -58,6 +59,27 @@ describe("bar geometry", () => {
});
describe("InlineDashboard", () => {
+ it("keeps token usage visible when its day has no known price", () => {
+ const unpriced = inlineDashboardModel({
+ providerId: "codex",
+ summary: {
+ todayCost: null, thirtyDayCost: null, thirtyDayTokens: 1000,
+ periodCost: null, periodTokens: 1000, reportingPeriod: "rolling:30",
+ latestTokens: 1000, topModel: null, estimateNote: "", tokenCostUpdatedAtMs: 0,
+ },
+ costHistory: [{ date: "2026-10-21", value: null }],
+ tokensHistory: [{ date: "2026-10-21", tokens: 1000 }],
+ quotaHistory: null,
+ t: (key) => key === "UsageSpendUnpriced" ? "Unpriced" : key,
+ })!;
+ const { container } = render();
+ const bar = container.querySelector(".card-dash__bar")!;
+ expect(Number(bar.style.opacity)).toBeGreaterThan(0);
+ expect(parseFloat(bar.style.height)).toBeGreaterThanOrEqual(3);
+ expect(bar.title).toContain("Unpriced");
+ expect(bar.title).not.toContain("$0");
+ });
+
it("renders the KPIs, the chart, the recent windows and the detail lines", () => {
const { container } = render();
expect(screen.getByText("Today")).toHaveClass("card-dash__kpi-title");
diff --git a/apps/desktop-tauri/src/components/card/InlineDashboard.tsx b/apps/desktop-tauri/src/components/card/InlineDashboard.tsx
index d4595e23967..35c2fddd3d5 100644
--- a/apps/desktop-tauri/src/components/card/InlineDashboard.tsx
+++ b/apps/desktop-tauri/src/components/card/InlineDashboard.tsx
@@ -54,12 +54,15 @@ function UsageBars({
setHovered(index)}
style={{
- height: `${barHeight(point.value, maxValue)}px`,
- opacity: barOpacity(point.value, maxValue),
+ height: `${point.hasUnpricedUsage ? 3 : barHeight(point.value, maxValue)}px`,
+ opacity: point.hasUnpricedUsage ? 0.55 : barOpacity(point.value, maxValue),
+ outline: point.hasUnpricedUsage ? "1px dashed currentColor" : undefined,
+ background: point.hasUnpricedUsage ? "transparent" : undefined,
}}
/>
))}
diff --git a/apps/desktop-tauri/src/lib/inlineDashboard.ts b/apps/desktop-tauri/src/lib/inlineDashboard.ts
index 365e977317f..d77789cfea9 100644
--- a/apps/desktop-tauri/src/lib/inlineDashboard.ts
+++ b/apps/desktop-tauri/src/lib/inlineDashboard.ts
@@ -24,6 +24,7 @@ export interface DashboardKpi {
export interface DashboardPoint {
date: string;
value: number | null;
+ hasUnpricedUsage: boolean;
title: string;
}
@@ -180,13 +181,16 @@ function chartPoints(input: InlineDashboardInput): DashboardPoint[] {
const tokensByDate = new Map(input.tokensHistory.map((point) => [point.date, point.tokens]));
return input.costHistory.slice(-CHART_DAYS).map((point) => {
const tokens = tokensByDate.get(point.date);
+ const hasUnpricedUsage = point.value == null && tokens != null && tokens > 0;
+ const cost = hasUnpricedUsage ? input.t("UsageSpendUnpriced")
+ : point.value == null ? "—" : formatCostAmount(point.value, "USD");
const title =
point.value == null && tokens == null
? point.date
- : `${point.date}: ${point.value == null ? "—" : formatCostAmount(point.value, "USD")} · ${
+ : `${point.date}: ${cost} · ${
tokens == null ? "—" : `${compactCount(tokens)} ${input.t("UsageSpendTokens")}`
}`;
- return { date: point.date, value: point.value, title };
+ return { date: point.date, value: point.value, hasUnpricedUsage, title };
});
}