Skip to content
Open
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
23 changes: 23 additions & 0 deletions apps/desktop-tauri/src/components/MenuCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ describe("MenuCard", () => {
buildBundle({
ActionCopyError: "Copy error",
ApiSpendTitle: "API spend",
AtlasCloudAvailableBalance: "Available balance",
AtlasCloudBalance: "Atlas Cloud balance",
DetailPaceRunsOutIn: "Runs out in",
PanelEstimatedFromLocalLogs: "Estimated from local logs",
PanelLeftSuffix: "left",
Expand Down Expand Up @@ -601,6 +603,27 @@ describe("MenuCard", () => {
expect(details.open).toBe(true);
});

it("renders provider display details once and hides them in compact overview", async () => {
const snapshot = provider(null);
snapshot.displayDetails = [
{
id: "atlascloud-available",
title: "Available balance",
value: "$95.50",
secondaryValue: null,
progress: null,
},
];

const detailed = renderCard(snapshot);
expect(await screen.findByText("Available balance: $95.50")).toBeInTheDocument();
expect(screen.getAllByText("Available balance: $95.50")).toHaveLength(1);
detailed.unmount();

renderCard(snapshot, { compactOverview: true });
expect(screen.queryByText("Available balance: $95.50")).not.toBeInTheDocument();
});

it("places Claude accounts above metrics and the collapsed usage details", async () => {
tauriMocks.claudeAccountsList.mockResolvedValue([
{ id: "a", email: "a@example.com", organization: "Personal", isActive: true, isSaved: true },
Expand Down
36 changes: 22 additions & 14 deletions apps/desktop-tauri/src/components/MenuCardDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import { SimpleBarChart, StackedBarChart } from "./MiniBarChart";
import { InventoryItemRow } from "./InventoryRows";
import { QuotaWindowHistory } from "./QuotaWindowHistory";
import { getPaceBudget, type PaceBudget } from "../lib/paceBudget";
import {
providerCostPeriodTitle,
providerDisplayDetailTitle,
} from "../lib/providerLabels";
import PaceDetailsChart from "./PaceDetailsChart";

/** Format a reserve description from raw pace data at render time. */
Expand Down Expand Up @@ -541,6 +545,11 @@ export default function MenuCardDetails({
);
const localCostHistory = chartData?.costHistory ?? [];
const costStyle = display.costSummaryDisplayStyle ?? "detailed";
const costPeriod = providerCostPeriodTitle(
provider.providerId,
provider.cost?.period ?? "",
t,
);

const {
hasMetrics,
Expand Down Expand Up @@ -598,15 +607,7 @@ export default function MenuCardDetails({
{!provider.error && hasDisplayDetails && !compactOverview && (
<section className="menu-card__group menu-card__provider-details">
{provider.displayDetails?.map((detail, index) => (
<DisplayDetailRow key={`${detail.id}-${index}`} detail={detail} />
))}
</section>
)}

{!provider.error && hasDisplayDetails && (
<section className="menu-card__group menu-card__provider-details">
{provider.displayDetails?.map((detail, index) => (
<DisplayDetailRow key={`${detail.id}-${index}`} detail={detail} />
<DisplayDetailRow key={`${detail.id}-${index}`} detail={detail} t={t} />
))}
</section>
)}
Expand All @@ -621,8 +622,8 @@ export default function MenuCardDetails({
{provider.cost.alwaysVisible === true && (provider.cost.limit ?? 0) <= 0
? t("ApiSpendTitle")
: provider.cost.balance != null && provider.cost.limit == null
? provider.cost.period || t("CreditsLabel")
: `${t("DetailCostTitle")} — ${provider.cost.period}`}
? costPeriod || t("CreditsLabel")
: `${t("DetailCostTitle")} — ${costPeriod}`}
</div>
{provider.cost.balance != null && provider.cost.limit == null ? (
<div className="menu-card__cost-line">
Expand Down Expand Up @@ -787,7 +788,14 @@ export default function MenuCardDetails({
);
}

function DisplayDetailRow({ detail }: { detail: ProviderDisplayDetail }) {
function DisplayDetailRow({
detail,
t,
}: {
detail: ProviderDisplayDetail;
t: (key: LocaleKey) => string;
}) {
const title = providerDisplayDetailTitle(detail, t);
const progress = detail.progress;
const progressPercent = progress && Number.isFinite(progress.used) && Number.isFinite(progress.total) && progress.total > 0
? Math.max(0, Math.min(100, (progress.used / progress.total) * 100))
Expand All @@ -796,13 +804,13 @@ function DisplayDetailRow({ detail }: { detail: ProviderDisplayDetail }) {
return (
<div className="menu-card__provider-detail">
<div className="menu-card__cost-line">
<span>{detail.title}: {detail.value}</span>
<span>{title}: {detail.value}</span>
{detail.secondaryValue && (
<span className="menu-card__cost-line--muted">{detail.secondaryValue}</span>
)}
</div>
{progressPercent != null && (
<div className="menu-metric__bar" aria-label={`${detail.title} progress`}>
<div className="menu-metric__bar" aria-label={`${title} progress`}>
<div className="menu-metric__bar-fill" style={{ width: `${progressPercent}%` }} />
</div>
)}
Expand Down
9 changes: 7 additions & 2 deletions apps/desktop-tauri/src/components/ProviderDisplayRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { ProviderDisplayDetail } from "../types/bridge";
import type { LocaleKey } from "../i18n/keys";
import { providerDisplayDetailTitle } from "../lib/providerLabels";

/**
* One transient provider detail line: "{title}: {value} [secondary]"
Expand All @@ -13,13 +15,16 @@ export function ProviderDisplayRow({
secondaryClassName,
trackClassName,
fillClassName,
t,
}: {
detail: ProviderDisplayDetail;
lineClassName: string;
secondaryClassName?: string;
trackClassName: string;
fillClassName: string;
t: (key: LocaleKey) => string;
}) {
const title = providerDisplayDetailTitle(detail, t);
const progress = detail.progress;
const progressPercent =
progress &&
Expand All @@ -32,13 +37,13 @@ export function ProviderDisplayRow({
return (
<div>
<div className={lineClassName}>
<span>{detail.title}: {detail.value}</span>
<span>{title}: {detail.value}</span>
{detail.secondaryValue && secondaryClassName && (
<span className={secondaryClassName}>{detail.secondaryValue}</span>
)}
</div>
{progressPercent != null && (
<div className={trackClassName} aria-label={`${detail.title} progress`}>
<div className={trackClassName} aria-label={`${title} progress`}>
<div className={fillClassName} style={{ width: `${progressPercent}%` }} />
</div>
)}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ describe("provider icon registry", () => {
}
});

it("ships the upstream Atlas Cloud glyph tinted by the brand color", () => {
const svg = PROVIDER_ICON_REGISTRY.atlascloud.svgPath;
expect(svg).toContain("<svg");
expect(svg).toContain('fill="currentColor"');
expect(svg).not.toContain('fill="#000"');
});

it("does not expose the retired Crof provider", () => {
expect(PROVIDER_ICON_REGISTRY).not.toHaveProperty("crof");
});
Expand Down
4 changes: 3 additions & 1 deletion apps/desktop-tauri/src/components/providers/providerIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import abacus from "./icons/ProviderIcon-abacus.svg?raw";
import alibaba from "./icons/ProviderIcon-alibaba.svg?raw";
import amp from "./icons/ProviderIcon-amp.svg?raw";
import antigravity from "./icons/ProviderIcon-antigravity.svg?raw";
import atlascloud from "./icons/ProviderIcon-atlascloud.svg?raw";
import augment from "./icons/ProviderIcon-augment.svg?raw";
import bedrock from "./icons/ProviderIcon-bedrock.svg?raw";
import claude from "./icons/ProviderIcon-claude.svg?raw";
Expand Down Expand Up @@ -93,6 +94,7 @@ const RAW: Record<string, string> = {
alibaba: tint(alibaba),
amp: tint(amp),
antigravity: tint(antigravity),
atlascloud: tint(atlascloud),
augment: tint(augment),
bedrock: tint(bedrock),
claude: tint(claude),
Expand Down Expand Up @@ -218,7 +220,7 @@ export const PROVIDER_ICON_REGISTRY: Record<string, ProviderIcon> = {
nanogpt: { id: "nanogpt", brandColor: "#687fa1", fallbackLetter: "N" },
infini: { id: "infini", brandColor: "#687fa1", fallbackLetter: "I" },
abacus: { id: "abacus", brandColor: "#7c3aed", fallbackLetter: "A", svgPath: RAW.abacus },
atlascloud: { id: "atlascloud", brandColor: "#5975F5", fallbackLetter: "A" },
atlascloud: { id: "atlascloud", brandColor: "#5975F5", fallbackLetter: "A", svgPath: RAW.atlascloud },
manus: { id: "manus", brandColor: "#34322d", fallbackLetter: "M", svgPath: RAW.manus },
mimo: { id: "mimo", brandColor: "#ff6900", fallbackLetter: "M", svgPath: RAW.mimo },
doubao: { id: "doubao", brandColor: "#2563eb", fallbackLetter: "D", svgPath: RAW.doubao },
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop-tauri/src/i18n/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export const ALL_LOCALE_KEYS = [
"ProviderDisabled",
"ProviderInfo",
"ProviderUsage",
"AtlasCloudAvailableBalance",
"AtlasCloudBalance",
"PanelUsageDetails",
"AuthType",
"DataSource",
Expand Down
38 changes: 38 additions & 0 deletions apps/desktop-tauri/src/lib/providerLabels.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import type { LocaleKey } from "../i18n/keys";
import type { ProviderDisplayDetail } from "../types/bridge";
import {
providerCostPeriodTitle,
providerDisplayDetailTitle,
} from "./providerLabels";

const translate = (key: LocaleKey) => `translated:${key}`;

describe("provider labels", () => {
it("localizes Atlas Cloud display labels and preserves other provider titles", () => {
const atlasBalance: ProviderDisplayDetail = {
id: "atlascloud-available",
title: "Available balance",
value: "$95.50",
secondaryValue: null,
progress: null,
};
const other: ProviderDisplayDetail = {
...atlasBalance,
id: "other",
title: "Credits",
};

expect(providerDisplayDetailTitle(atlasBalance, translate)).toBe(
"translated:AtlasCloudAvailableBalance",
);
expect(providerDisplayDetailTitle(other, translate)).toBe("Credits");
});

it("localizes Atlas Cloud balance period and preserves other periods", () => {
expect(
providerCostPeriodTitle("atlascloud", "Atlas Cloud balance", translate),
).toBe("translated:AtlasCloudBalance");
expect(providerCostPeriodTitle("other", "This month", translate)).toBe("This month");
});
});
29 changes: 29 additions & 0 deletions apps/desktop-tauri/src/lib/providerLabels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { LocaleKey } from "../i18n/keys";
import type { ProviderDisplayDetail } from "../types/bridge";

type Translate = (key: LocaleKey) => string;

const DISPLAY_DETAIL_TITLE_KEYS: ReadonlyMap<string, LocaleKey> = new Map<string, LocaleKey>([
["atlascloud-available", "AtlasCloudAvailableBalance"],
]);

const COST_PERIOD_KEYS: ReadonlyMap<string, LocaleKey> = new Map<string, LocaleKey>([
["atlascloud:Atlas Cloud balance", "AtlasCloudBalance"],
]);

export function providerDisplayDetailTitle(
detail: ProviderDisplayDetail,
t: Translate,
): string {
const key = DISPLAY_DETAIL_TITLE_KEYS.get(detail.id);
return key ? t(key) : detail.title;
}

export function providerCostPeriodTitle(
providerId: string,
period: string,
t: Translate,
): string {
const key = COST_PERIOD_KEYS.get(`${providerId}:${period}`);
return key ? t(key) : period;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import type { CostSnapshotBridge } from "../../../../types/bridge";
import type { LocaleKey } from "../../../../i18n/keys";
import { CostSection } from "./CostSection";

describe("CostSection", () => {
it("does not present the zero usage carrier as spend for balance-only providers", () => {
const cost: CostSnapshotBridge = {
used: 0,
limit: null,
remaining: null,
currencyCode: "USD",
period: "Atlas Cloud balance",
resetsAt: null,
formattedUsed: "$0.00",
formattedLimit: null,
balance: 95.5,
formattedBalance: "$95.50",
};

const { container } = render(
<CostSection cost={cost} t={(key: LocaleKey) => key} />,
);

expect(container.firstChild).toBeNull();
expect(screen.queryByText("DetailCostUsed")).toBeNull();
expect(screen.queryByText("$0.00")).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ interface Props {
export function CostSection({ cost, t }: Props) {
if (!cost) return null;

const balanceOnly =
cost.balance != null &&
cost.limit == null &&
cost.used === 0 &&
(cost.daily?.length ?? 0) === 0;
if (balanceOnly) return null;

const rows: { label: string; value: string | null }[] = [
{ label: t("DetailCostUsed"), value: cost.formattedUsed },
{ label: t("DetailCostLimit"), value: cost.formattedLimit },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export function UsageSection({ provider, resetTimeRelative, t }: Props) {
<ProviderDisplayRow
key={detail.id}
detail={detail}
t={t}
lineClassName="provider-usage-inventory"
trackClassName="provider-usage-bar__track"
fillClassName="provider-usage-bar__fill"
Expand Down Expand Up @@ -163,4 +164,4 @@ function UsageBar({
)}
</div>
);
}
}
2 changes: 2 additions & 0 deletions rust/src/locale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ locale_keys! {
ProviderDisabled,
ProviderInfo,
ProviderUsage,
AtlasCloudAvailableBalance,
AtlasCloudBalance,
PanelUsageDetails,
AuthType,
DataSource,
Expand Down
2 changes: 2 additions & 0 deletions rust/src/locale/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ ProviderEnabled = Enabled
ProviderDisabled = Disabled
ProviderInfo = Info
ProviderUsage = Usage
AtlasCloudAvailableBalance = Available balance
AtlasCloudBalance = Atlas Cloud balance
PanelUsageDetails = Usage details
AuthType = Authentication
DataSource = Data Source
Expand Down
2 changes: 2 additions & 0 deletions rust/src/locale/es-MX.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ProviderEnabled = Habilitado
ProviderDisabled = Deshabilitado
ProviderInfo = Información
ProviderUsage = Uso
AtlasCloudAvailableBalance = Saldo disponible
AtlasCloudBalance = Saldo de Atlas Cloud
AuthType = Autenticación
DataSource = Fuente de datos
ProviderNotDetected = no detectado
Expand Down
2 changes: 2 additions & 0 deletions rust/src/locale/ja-JP.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ProviderEnabled = 有効
ProviderDisabled = 無効
ProviderInfo = 情報
ProviderUsage = 使用量
AtlasCloudAvailableBalance = 利用可能残高
AtlasCloudBalance = Atlas Cloud の残高
AuthType = 認証
DataSource = データソース
ProviderNotDetected = 未検出
Expand Down
2 changes: 2 additions & 0 deletions rust/src/locale/ko-KR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ProviderEnabled = 활성화됨
ProviderDisabled = 비활성화됨
ProviderInfo = 정보
ProviderUsage = 사용량
AtlasCloudAvailableBalance = 사용 가능 잔액
AtlasCloudBalance = Atlas Cloud 잔액
AuthType = 인증
DataSource = 데이터 소스
ProviderNotDetected = 감지되지 않음
Expand Down
2 changes: 2 additions & 0 deletions rust/src/locale/ru-RU.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ ProviderEnabled = Включено
ProviderDisabled = Отключено
ProviderInfo = Информация
ProviderUsage = Использование
AtlasCloudAvailableBalance = Доступный баланс
AtlasCloudBalance = Баланс Atlas Cloud
AuthType = Аутентификация
DataSource = Источник данных
ProviderNotDetected = не обнаружено
Expand Down
Loading