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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
reconcileContributionSelectionRange,
resolveContributionRange,
resolveContributionSelectedDate,
reverseContributionCalendarWeeks,
} from "../../src/components/profile/ProfileContributionGraph";
import { BOX_BORDER_RADIUS, BOX_WIDTH } from "../../src/lib/constants";
import type { DailyContribution } from "../../src/lib/types";
Expand Down Expand Up @@ -596,4 +597,42 @@ describe("profile contribution calendar", () => {
}
}
});

it("mirrors 2D calendar weeks so the newest week renders first", () => {
const calendar = createContributionCalendar(
[contribution("2026-06-15", 200, 1)],
"2026-05-01",
"2026-07-31",
);

const reversed = reverseContributionCalendarWeeks(
calendar.cells,
calendar.monthMarkers,
calendar.weekCount,
);

// Same cells, regrouped so the newest chronological week leads.
expect(calendar.cells.length % 7).toBe(0);
expect(reversed.cells).toHaveLength(calendar.cells.length);
expect(reversed.cells.slice(0, 7)).toEqual(calendar.cells.slice(-7));
expect(reversed.cells.slice(-7)).toEqual(calendar.cells.slice(0, 7));

// Month markers move onto the mirrored week columns.
expect(calendar.monthMarkers.length).toBeGreaterThan(0);
expect(reversed.monthMarkers).toEqual(
calendar.monthMarkers.map((marker) => ({
...marker,
weekIndex: calendar.weekCount - 1 - marker.weekIndex,
})),
);

// Reversing twice is the identity transform (the non-reversed rendering).
const roundTrip = reverseContributionCalendarWeeks(
reversed.cells,
reversed.monthMarkers,
calendar.weekCount,
);
expect(roundTrip.cells).toEqual(calendar.cells);
expect(roundTrip.monthMarkers).toEqual(calendar.monthMarkers);
});
});
127 changes: 127 additions & 0 deletions packages/frontend/__tests__/components/profileUsageChartData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import type {
import {
BLANK_USAGE_MODEL,
DAILY_REMAINDER_USAGE_MODEL,
MAX_LEGEND_MODELS,
OTHER_USAGE_PROVIDERS,
PROVIDER_REMAINDER_USAGE_MODEL,
UNATTRIBUTED_USAGE_PROVIDER,
aggregateDailyUsage,
buildUsageChartData,
fillMissingUsageDays,
getActiveTooltipRows,
selectLegendModels,
sumTokenBreakdown,
toTrailingAverage,
} from "../../src/components/profile/usageChartData";
Expand Down Expand Up @@ -735,3 +737,128 @@ describe("profile usage chart aggregation", () => {
);
});
});

describe("profile usage chart legend", () => {
it("ranks models by total descending and cuts off at the limit", () => {
const chart = buildUsageChartData(
aggregateDailyUsage([
day("2026-08-01", [
client("claude", { input: 100 }, 10, "m-a"),
client("claude", { input: 80 }, 8, "m-b"),
client("claude", { input: 60 }, 6, "m-c"),
client("claude", { input: 40 }, 4, "m-d"),
]),
]),
"tokens",
"all",
"daily",
);

const { visible, hiddenCount } = selectLegendModels(chart.series, 2);

expect(visible.map(({ label }) => label)).toEqual(["m-a", "m-b"]);
expect(hiddenCount).toBe(2);
});

it("excludes remainder, Other, blank, and daily-remainder series", () => {
const nested = buildUsageChartData(
aggregateDailyUsage([
day("2026-08-02", [
nestedClient(
"claude",
{
opus: model({ input: 40 }, 4),
"": model({ input: 10 }, 1),
"<synthetic>": model({ input: 5 }, 0.5),
},
{ input: 100 },
10,
),
]),
]),
"tokens",
"all",
"daily",
);
const legacyDay = day("2026-08-03", []);
legacyDay.totals.tokens = 500;
legacyDay.totals.cost = 5;
const legacy = buildUsageChartData(
aggregateDailyUsage([legacyDay]),
"tokens",
"all",
"daily",
);
const capped = buildUsageChartData(
aggregateDailyUsage([
day("2026-08-04", [
client("codex", { input: 30 }, 3, "r1"),
client("codex", { input: 20 }, 2, "r2"),
client("codex", { input: 10 }, 1, "r3"),
]),
]),
"tokens",
"all",
"daily",
30,
2,
);

// The nested client exposes a real model, a blank model, a synthetic
// model, and a provider remainder; the legacy day exposes a daily
// remainder; the capped chart exposes an "Other" series remainder.
expect(nested.series.map(({ kind }) => kind)).toEqual(
expect.arrayContaining([
"model",
"blank-model",
"synthetic",
"provider-remainder",
]),
);
expect(legacy.series.some(({ kind }) => kind === "daily-remainder")).toBe(
true,
);
expect(capped.series.some(({ kind }) => kind === "series-remainder")).toBe(
true,
);

const combined = [
...nested.series,
...legacy.series,
...capped.series,
];
const { visible } = selectLegendModels(combined, 10);

expect(new Set(visible.map(({ label }) => label))).toEqual(
new Set(["opus", "Synthetic", "r1"]),
);
});

it("disambiguates duplicate model labels across providers", () => {
const chart = buildUsageChartData(
aggregateDailyUsage([
day("2026-08-05", [
client("codex", { input: 10 }, 1, "shared-model"),
client("claude", { input: 20 }, 2, "shared-model"),
]),
]),
"tokens",
"all",
"daily",
);

const { visible } = selectLegendModels(chart.series, MAX_LEGEND_MODELS);
const claudeSeries = chart.series.find(
({ provider }) => provider === "claude",
);
const codexSeries = chart.series.find(
({ provider }) => provider === "codex",
);

expect(visible.map(({ label }) => label)).toEqual([
`shared-model · ${claudeSeries?.providerLabel}`,
`shared-model · ${codexSeries?.providerLabel}`,
]);
expect(new Set(visible.map(({ label }) => label)).size).toBe(2);
});
});
15 changes: 12 additions & 3 deletions packages/frontend/src/components/SourceLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ interface SourceLogoProps {
sourceId: string;
height?: number;
className?: string;
/**
* When the logo sits next to a visible text label of the same source, mark it
* decorative so assistive tech doesn't announce the source name twice.
*/
decorative?: boolean;
}

const StyledImg = styled.img<{ $height: number }>`
Expand All @@ -21,20 +26,24 @@ const StyledImg = styled.img<{ $height: number }>`
max-height: ${props => props.$height}px;
`;

export function SourceLogo({ sourceId, height = 14, className = "" }: SourceLogoProps) {
export function SourceLogo({ sourceId, height = 14, className = "", decorative = false }: SourceLogoProps) {
const normalizedId = sourceId.toLowerCase() as ClientType;
const src = Object.prototype.hasOwnProperty.call(SOURCE_LOGOS, normalizedId)
? SOURCE_LOGOS[normalizedId]
: null;

if (!src) {
return <span className={className}>{sourceId}</span>;
return (
<span className={className} aria-hidden={decorative || undefined}>
{sourceId}
</span>
);
}

return (
<StyledImg
src={src}
alt={sourceId}
alt={decorative ? "" : sourceId}
$height={height}
className={className}
/>
Expand Down
Loading