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
123 changes: 115 additions & 8 deletions apps/desktop-tauri/src/floatbar/FloatBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,32 @@ vi.mock("@tauri-apps/api/core", () => coreMocks);
import FloatBar from "./FloatBar";
import { LocaleProvider } from "../i18n/LocaleProvider";
import { buildBundle } from "../test/localeHarness";
import type { BootstrapState, ProviderUsageSnapshot, SettingsSnapshot } from "../types/bridge";
import type {
BootstrapState,
ProviderUsageSnapshot,
RateWindowSnapshot,
SettingsSnapshot,
} from "../types/bridge";

type RateWindowOptions = {
exhausted?: boolean;
informational?: boolean;
resetsAt?: string | null;
resetDescription?: string | null;
};

function rateWindow(
used: number,
opts: {
exhausted?: boolean;
resetsAt?: string | null;
resetDescription?: string | null;
} = {},
) {
opts: RateWindowOptions = {},
): RateWindowSnapshot {
return {
usedPercent: used,
remainingPercent: 100 - used,
windowMinutes: null,
resetsAt: opts.resetsAt ?? null,
resetDescription: opts.resetDescription ?? null,
isExhausted: opts.exhausted ?? false,
isInformational: opts.informational,
reservePercent: null,
reserveDescription: null,
};
Expand All @@ -66,13 +75,23 @@ function snapshot(
error?: string | null;
resetsAt?: string | null;
resetDescription?: string | null;
informational?: boolean;
secondary?: {
used: number;
exhausted?: boolean;
informational?: boolean;
resetsAt?: string | null;
resetDescription?: string | null;
};
} = {},
): ProviderUsageSnapshot {
return {
providerId: id,
displayName: display,
primary: rateWindow(used, opts),
secondary: null,
secondary: opts.secondary
? rateWindow(opts.secondary.used, opts.secondary)
: null,
modelSpecific: null,
tertiary: null,
extraRateWindows: [],
Expand Down Expand Up @@ -214,6 +233,94 @@ describe("FloatBar", () => {
expect(titles[1]).toMatch(/Claude: 20% used/);
});

it("keeps a normal primary window when a secondary window is available", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 20, { secondary: { used: 90 } }),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(settings());

const { container } = renderFloatBar(bootstrap());
await waitFor(() => {
expect(container.querySelector(".floatbar__pill")?.getAttribute("title")).toContain(
"Claude: 20% used",
);
});
});

it("uses a real secondary window when the primary window is informational", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 10, {
informational: true,
secondary: {
used: 80,
resetsAt: null,
resetDescription: "Resets in 2 hours",
},
}),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(
settings({ floatBarShowResetInline: true }),
);

const { container } = renderFloatBar(bootstrap({ floatBarShowResetInline: true }));
await waitFor(() => {
const pill = container.querySelector(".floatbar__pill");
expect(pill?.getAttribute("title")).toContain("Claude: 80% used\nResets in 2 hours");
expect(pill?.classList.contains("floatbar__pill--warn")).toBe(true);
expect(container.querySelector(".floatbar__reset")?.textContent).toContain("2 hours");
});
});

it("keeps an informational primary window when no secondary window is available", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 10, { informational: true }),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(settings());

const { container } = renderFloatBar(bootstrap());
await waitFor(() => {
expect(container.querySelector(".floatbar__pill")?.getAttribute("title")).toContain(
"Claude: 10% used",
);
});
});

it("keeps an informational primary window when the secondary window is informational", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 10, {
informational: true,
secondary: { used: 90, informational: true },
}),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(settings());

const { container } = renderFloatBar(bootstrap());
await waitFor(() => {
expect(container.querySelector(".floatbar__pill")?.getAttribute("title")).toContain(
"Claude: 10% used",
);
});
});

it("sorts providers by their effective rate window", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("claude", "Claude", 90, {
informational: true,
secondary: { used: 20 },
}),
snapshot("codex", "Codex", 50),
]);
tauriMocks.getSettingsSnapshot.mockResolvedValue(settings());

const { container } = renderFloatBar(bootstrap());
await waitFor(() => {
const titles = Array.from(container.querySelectorAll(".floatbar__pill")).map(
(pill) => pill.getAttribute("title"),
);
expect(titles).toEqual(["Codex: 50% used", "Claude: 20% used"]);
});
});

it("loads local cost summaries without using the foreground chart endpoint", async () => {
tauriMocks.getCachedProviders.mockResolvedValue([
snapshot("codex", "Codex", 75),
Expand Down
18 changes: 12 additions & 6 deletions apps/desktop-tauri/src/floatbar/FloatBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getSettingsSnapshot,
refreshProvidersIfStale,
} from "../lib/tauri";
import { selectSingleMetricUsageWindow } from "../lib/usageWindows";
import { ProviderIcon } from "../components/providers/ProviderIcon";
import { getProviderIcon } from "../components/providers/providerIcons";
import type {
Expand Down Expand Up @@ -178,20 +179,21 @@ function ProviderPill({
usedSuffix: string;
remainingSuffix: string;
}) {
const remaining = Math.max(0, Math.min(100, provider.primary.remainingPercent));
const used = Math.max(0, Math.min(100, provider.primary.usedPercent));
const rateWindow = selectSingleMetricUsageWindow(provider);
const remaining = Math.max(0, Math.min(100, rateWindow.remainingPercent));
const used = Math.max(0, Math.min(100, rateWindow.usedPercent));
const displayPercent = showAsUsed ? used : remaining;
const displaySuffix = showAsUsed ? usedSuffix : remainingSuffix;
const exhausted = provider.primary.isExhausted || provider.error;
const exhausted = rateWindow.isExhausted || provider.error;
let tone: "ok" | "warn" | "crit" = "ok";
if (exhausted || remaining <= critRemaining) tone = "crit";
else if (remaining <= highRemaining) tone = "warn";

const brand = getProviderIcon(provider.providerId).brandColor;
const label = provider.error ? "—" : `${Math.round(displayPercent)}%`;
const resetText = useFormattedResetTime(
provider.primary.resetsAt,
provider.primary.resetDescription,
rateWindow.resetsAt,
rateWindow.resetDescription,
resetRelative,
);
const resetSuffix = resetText ? `\n${resetText}` : "";
Expand Down Expand Up @@ -308,7 +310,11 @@ export default function FloatBar({ state }: { state: BootstrapState }) {
const wanted = new Set(filterIds);
list = list.filter((p) => wanted.has(p.providerId));
}
return [...list].sort((a, b) => b.primary.usedPercent - a.primary.usedPercent);
return [...list].sort(
(a, b) =>
selectSingleMetricUsageWindow(b).usedPercent -
selectSingleMetricUsageWindow(a).usedPercent,
);
}, [providers, settings.enabledProviders, filterIds]);

const visibleCostTargets = useMemo<FloatBarCostTarget[]>(
Expand Down
48 changes: 48 additions & 0 deletions apps/desktop-tauri/src/lib/usageWindows.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import type { RateWindowSnapshot } from "../types/bridge";
import { selectSingleMetricUsageWindow } from "./usageWindows";

function rateWindow(
overrides: Partial<RateWindowSnapshot> = {},
): RateWindowSnapshot {
return {
usedPercent: 0,
remainingPercent: 100,
windowMinutes: null,
resetsAt: null,
resetDescription: null,
isExhausted: false,
reservePercent: null,
reserveDescription: null,
...overrides,
};
}

describe("selectSingleMetricUsageWindow", () => {
it("returns a normal primary when a secondary exists", () => {
const primary = rateWindow();
const secondary = rateWindow({ isInformational: true });

expect(selectSingleMetricUsageWindow({ primary, secondary })).toBe(primary);
});

it("returns a real secondary when the primary is informational", () => {
const primary = rateWindow({ isInformational: true });
const secondary = rateWindow();

expect(selectSingleMetricUsageWindow({ primary, secondary })).toBe(secondary);
});

it("returns an informational primary when the secondary is null", () => {
const primary = rateWindow({ isInformational: true });

expect(selectSingleMetricUsageWindow({ primary, secondary: null })).toBe(primary);
});

it("returns an informational primary when the secondary is informational", () => {
const primary = rateWindow({ isInformational: true });
const secondary = rateWindow({ isInformational: true });

expect(selectSingleMetricUsageWindow({ primary, secondary })).toBe(primary);
});
});
14 changes: 14 additions & 0 deletions apps/desktop-tauri/src/lib/usageWindows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type {
ProviderUsageSnapshot,
RateWindowSnapshot,
} from "../types/bridge";

/** Selects the automatic window for surfaces rendering exactly one primary/secondary usage metric. */
export function selectSingleMetricUsageWindow(
provider: Pick<ProviderUsageSnapshot, "primary" | "secondary">,
): RateWindowSnapshot {
const { primary, secondary } = provider;
return primary.isInformational && secondary && !secondary.isInformational
? secondary
: primary;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import type {
ProviderCatalogEntry,
ProviderUsageSnapshot,
RateWindowSnapshot,
SettingsSnapshot,
} from "../../../types/bridge";

const hookMocks = vi.hoisted(() => ({
useProviders: vi.fn(),
}));

vi.mock("../../../hooks/useProviders", () => hookMocks);
vi.mock("../../../hooks/useLocale", () => ({
useLocale: () => ({ t: (key: string) => key }),
}));
vi.mock("../../../lib/tauri", () => ({
reorderProviders: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("../providers/ProviderDetailPane", () => ({
ProviderDetailPane: () => null,
}));

import ProvidersTab from "./ProvidersTab";

function rateWindow(
usedPercent: number,
isInformational?: boolean,
): RateWindowSnapshot {
return {
usedPercent,
remainingPercent: 100 - usedPercent,
windowMinutes: null,
resetsAt: null,
resetDescription: null,
isExhausted: false,
isInformational,
reservePercent: null,
reserveDescription: null,
};
}

const provider: ProviderCatalogEntry = {
id: "codex",
displayName: "Codex",
cookieDomain: null,
};

const settings = {
enabledProviders: [provider.id],
resetTimeRelative: true,
providerMetrics: {},
} as SettingsSnapshot;

describe("ProvidersTab", () => {
it("shows the real secondary percentage when the primary is informational", () => {
const snapshot: ProviderUsageSnapshot = {
providerId: provider.id,
displayName: provider.displayName,
primary: rateWindow(0, true),
secondary: rateWindow(42),
modelSpecific: null,
tertiary: null,
extraRateWindows: [],
cost: null,
planName: null,
accountEmail: null,
sourceLabel: "auto",
updatedAt: new Date().toISOString(),
error: null,
pace: null,
accountOrganization: null,
trayStatusLabel: null,
};
hookMocks.useProviders.mockReturnValue({ providers: [snapshot] });

render(
<ProvidersTab
settings={settings}
providers={[provider]}
set={vi.fn()}
saving={false}
/>,
);

expect(screen.getByText("42%")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "../providers/ProvidersSidebar";
import { ProviderDetailPane } from "../providers/ProviderDetailPane";
import { reorderProviders } from "../../../lib/tauri";
import { selectSingleMetricUsageWindow } from "../../../lib/usageWindows";
import { useProviders } from "../../../hooks/useProviders";

interface ProvidersTabProps {
Expand Down Expand Up @@ -262,8 +263,7 @@ function providerSidebarMetric(
snap: ProviderUsageSnapshot | null,
): string | undefined {
if (!snap) return undefined;
const rate = snap.primary;
if (!rate) return undefined;
const rate = selectSingleMetricUsageWindow(snap);
if (Number.isFinite(rate.usedPercent)) {
return `${Math.round(Math.max(0, rate.usedPercent))}%`;
}
Expand Down
Loading