Skip to content

Commit b0a0c85

Browse files
committed
Refine settings navigation and provider usage activity UI
- Add responsive token activity ranges, month labels, and intensity legend - Improve provider model and settings navigation layouts - Update settings navigation coverage
1 parent cbf0bb1 commit b0a0c85

10 files changed

Lines changed: 413 additions & 272 deletions

apps/web/src/components/ProviderUsageDashboard.tsx

Lines changed: 137 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import { Button } from "./ui/button";
1414
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
1515

1616
type TokenActivityMode = "daily" | "weekly" | "cumulative";
17+
type TokenActivityRange = "13-weeks" | "1-year";
1718

1819
const DAY_MS = 86_400_000;
1920
const ACTIVITY_MODE_LABELS: Record<TokenActivityMode, string> = {
2021
daily: "Daily",
2122
weekly: "Weekly",
2223
cumulative: "Cumulative",
2324
};
25+
const TOKEN_ACTIVITY_LEGEND_LEVELS = [0, 20, 40, 65, 90] as const;
2426

2527
interface TokenActivityCell {
2628
readonly dateKey: string;
@@ -109,13 +111,13 @@ function formatTokenActivityWeekTooltip(
109111
}
110112

111113
function tokenActivityColorClass(intensityPercent: number): string {
112-
if (intensityPercent <= 0) return "bg-muted/35";
113-
if (intensityPercent < 15) return "bg-primary/10";
114-
if (intensityPercent < 30) return "bg-primary/25";
115-
if (intensityPercent < 45) return "bg-primary/45";
116-
if (intensityPercent < 65) return "bg-primary/65";
117-
if (intensityPercent < 85) return "bg-primary/85";
118-
return "bg-primary";
114+
if (intensityPercent <= 0) return "bg-muted/70";
115+
if (intensityPercent < 15) return "bg-primary-graph/30";
116+
if (intensityPercent < 30) return "bg-primary-graph/45";
117+
if (intensityPercent < 45) return "bg-primary-graph/60";
118+
if (intensityPercent < 65) return "bg-primary-graph/75";
119+
if (intensityPercent < 85) return "bg-primary-graph/90";
120+
return "bg-primary-graph";
119121
}
120122

121123
function scaleTokenActivityIntensity(value: number, maxValue: number): number {
@@ -259,8 +261,8 @@ function TokenActivitySquare(props: { readonly cell: TokenActivityCell }) {
259261
role="img"
260262
aria-label={props.cell.tooltip}
261263
className={cn(
262-
"block aspect-square w-full min-w-0 cursor-default rounded-[var(--app-radius-tiny)] transition-[transform,filter,box-shadow] duration-150 ease-out",
263-
"hover:z-10 hover:-translate-y-px hover:scale-110 hover:brightness-125 hover:ring-1 hover:ring-primary/45",
264+
"block aspect-square w-full min-w-0 cursor-default rounded-[var(--app-radius-tiny)] transition-[filter] duration-150 ease-out",
265+
"hover:brightness-125",
264266
tokenActivityColorClass(props.cell.intensityPercent),
265267
)}
266268
/>
@@ -283,7 +285,7 @@ function TokenActivityWeekColumn(props: {
283285
}) {
284286
if (props.mode === "daily") {
285287
return (
286-
<div className="grid min-w-0 grid-rows-7 gap-1">
288+
<div className="grid min-w-0 grid-rows-7 gap-0.5">
287289
{props.week.cells.map((cell) => (
288290
<TokenActivitySquare key={cell.dateKey} cell={cell} />
289291
))}
@@ -300,15 +302,15 @@ function TokenActivityWeekColumn(props: {
300302
<div
301303
role="img"
302304
aria-label={props.week.tooltip}
303-
className="group grid min-w-0 cursor-default grid-rows-7 gap-1"
305+
className="group grid min-w-0 cursor-default grid-rows-7 gap-0.5"
304306
>
305307
{props.week.cells.map((cell) => (
306308
<span
307309
key={cell.dateKey}
308310
aria-hidden="true"
309311
className={cn(
310-
"block aspect-square w-full min-w-0 rounded-[var(--app-radius-tiny)] transition-[transform,filter,box-shadow] duration-150 ease-out",
311-
"group-hover:z-10 group-hover:scale-105 group-hover:brightness-125 group-hover:ring-1 group-hover:ring-primary/40",
312+
"block aspect-square w-full min-w-0 rounded-[var(--app-radius-tiny)] transition-[filter] duration-150 ease-out",
313+
"group-hover:brightness-125",
312314
tokenActivityColorClass(cell.intensityPercent),
313315
)}
314316
/>
@@ -327,6 +329,63 @@ function TokenActivityWeekColumn(props: {
327329
);
328330
}
329331

332+
function TokenActivityGrid(props: {
333+
readonly weeks: ReadonlyArray<TokenActivityWeek>;
334+
readonly mode: TokenActivityMode;
335+
readonly usageLabel: string;
336+
readonly className?: string | undefined;
337+
}) {
338+
return (
339+
<div className={cn("min-w-0 pb-1", props.className)}>
340+
<div
341+
className="grid min-w-0 gap-0.5 overflow-visible"
342+
style={{
343+
gridTemplateColumns: `repeat(${props.weeks.length}, minmax(0, 1fr))`,
344+
}}
345+
aria-label={`${props.usageLabel} ${ACTIVITY_MODE_LABELS[props.mode].toLowerCase()} token activity`}
346+
>
347+
{props.weeks.map((week) => (
348+
<TokenActivityWeekColumn key={week.weekKey} week={week} mode={props.mode} />
349+
))}
350+
</div>
351+
<div
352+
className="mt-2 grid min-w-0 gap-0.5"
353+
style={{
354+
gridTemplateColumns: `repeat(${props.weeks.length}, minmax(0, 1fr))`,
355+
}}
356+
aria-hidden
357+
>
358+
{props.weeks.map((week) => (
359+
<span
360+
key={week.weekKey}
361+
className="h-3 overflow-visible text-[10px] leading-none text-muted-foreground"
362+
>
363+
{week.monthLabel}
364+
</span>
365+
))}
366+
</div>
367+
<div
368+
className="mt-1.5 flex items-center justify-end gap-1 text-[10px] leading-none text-muted-foreground"
369+
aria-label="Token activity intensity from less to more"
370+
>
371+
<span>Less</span>
372+
<span className="flex items-center gap-0.5" aria-hidden>
373+
{TOKEN_ACTIVITY_LEGEND_LEVELS.map((level) => (
374+
<span
375+
key={level}
376+
className={cn(
377+
"size-2.5 rounded-[var(--app-radius-tiny)]",
378+
tokenActivityColorClass(level),
379+
)}
380+
/>
381+
))}
382+
</span>
383+
<span>More</span>
384+
</div>
385+
</div>
386+
);
387+
}
388+
330389
function UsageLimitBar(props: {
331390
readonly usageLabel: string;
332391
readonly window: ProviderAccountUsageWindowPresentation;
@@ -365,11 +424,14 @@ export function ProviderUsageDashboard(props: {
365424
readonly accountUsageResetInFlight?: boolean | undefined;
366425
}) {
367426
const [activityMode, setActivityMode] = useState<TokenActivityMode>("daily");
427+
const [activityRange, setActivityRange] = useState<TokenActivityRange>("13-weeks");
368428
const tokenUsage = props.usage.tokenUsage ?? null;
369429
const activityWeeks = useMemo(
370430
() => buildTokenActivityWeeks(tokenUsage?.buckets ?? [], activityMode),
371431
[activityMode, tokenUsage?.buckets],
372432
);
433+
const mobileActivityWeeks =
434+
activityRange === "13-weeks" ? activityWeeks.slice(-13) : activityWeeks;
373435
const showLimits = props.showLimits ?? true;
374436
const hasLimits =
375437
showLimits &&
@@ -464,16 +526,31 @@ export function ProviderUsageDashboard(props: {
464526
{tokenUsage ? (
465527
<section className="grid gap-4" aria-label={`${tokenUsage.label} activity`}>
466528
{tokenUsage.summary.length > 0 ? (
467-
<div className="grid overflow-hidden rounded-lg border border-border/70 sm:grid-cols-5">
468-
{tokenUsage.summary.map((entry) => (
469-
<div
470-
key={entry.key}
471-
className="min-w-0 border-border/60 border-t px-4 py-3 first:border-t-0 sm:border-t-0 sm:border-l sm:first:border-l-0"
472-
>
473-
<div className="truncate text-sm font-medium text-foreground">{entry.value}</div>
474-
<div className="mt-0.5 truncate text-xs text-muted-foreground">{entry.label}</div>
475-
</div>
476-
))}
529+
<div className="grid grid-cols-2 overflow-hidden rounded-lg border border-border/70 sm:grid-cols-5">
530+
{tokenUsage.summary.map((entry, index) => {
531+
const isFirstMobileRow = index < 2;
532+
const isRightMobileColumn = index % 2 === 1;
533+
const isLastUnpairedEntry =
534+
tokenUsage.summary.length % 2 === 1 && index === tokenUsage.summary.length - 1;
535+
return (
536+
<div
537+
key={entry.key}
538+
className={cn(
539+
"min-w-0 border-border/60 px-3 py-3 sm:col-span-1 sm:border-t-0 sm:border-l sm:px-4 sm:first:border-l-0",
540+
!isFirstMobileRow && "border-t",
541+
isRightMobileColumn && "border-l",
542+
isLastUnpairedEntry && "col-span-2",
543+
)}
544+
>
545+
<div className="truncate text-sm font-medium text-foreground">
546+
{entry.value}
547+
</div>
548+
<div className="mt-0.5 truncate text-xs text-muted-foreground">
549+
{entry.label}
550+
</div>
551+
</div>
552+
);
553+
})}
477554
</div>
478555
) : null}
479556

@@ -499,36 +576,46 @@ export function ProviderUsageDashboard(props: {
499576
))}
500577
</div>
501578
</div>
502-
{activityWeeks.length > 0 ? (
503-
<div className="min-w-0 pb-1">
504-
<div
505-
className="grid min-w-0 gap-1 overflow-visible"
506-
style={{
507-
gridTemplateColumns: `repeat(${activityWeeks.length}, minmax(0, 1fr))`,
508-
}}
509-
aria-label={`${tokenUsage.label} ${ACTIVITY_MODE_LABELS[activityMode].toLowerCase()} token activity`}
579+
<div
580+
role="group"
581+
className="inline-flex w-fit rounded-md border border-border/70 bg-muted/20 p-0.5 sm:hidden"
582+
aria-label="Token activity range"
583+
>
584+
{(["13-weeks", "1-year"] as const).map((range) => (
585+
<button
586+
key={range}
587+
type="button"
588+
className={cn(
589+
"h-7 cursor-pointer rounded px-2.5 text-xs transition-colors",
590+
activityRange === range
591+
? "bg-background text-foreground shadow-xs"
592+
: "text-muted-foreground hover:text-foreground",
593+
)}
594+
onClick={() => setActivityRange(range)}
595+
aria-pressed={activityRange === range}
510596
>
511-
{activityWeeks.map((week) => (
512-
<TokenActivityWeekColumn key={week.weekKey} week={week} mode={activityMode} />
513-
))}
597+
{range === "13-weeks" ? "13 weeks" : "1 year"}
598+
</button>
599+
))}
600+
</div>
601+
{activityWeeks.length > 0 ? (
602+
<>
603+
<div className="overflow-x-auto sm:hidden">
604+
<TokenActivityGrid
605+
weeks={mobileActivityWeeks}
606+
mode={activityMode}
607+
usageLabel={tokenUsage.label}
608+
className={activityRange === "1-year" ? "min-w-[40rem]" : undefined}
609+
/>
514610
</div>
515-
<div
516-
className="mt-2 grid min-w-0 gap-1"
517-
style={{
518-
gridTemplateColumns: `repeat(${activityWeeks.length}, minmax(0, 1fr))`,
519-
}}
520-
aria-hidden
521-
>
522-
{activityWeeks.map((week) => (
523-
<span
524-
key={week.weekKey}
525-
className="h-3 overflow-visible text-[10px] leading-none text-muted-foreground/75"
526-
>
527-
{week.monthLabel}
528-
</span>
529-
))}
611+
<div className="hidden sm:block">
612+
<TokenActivityGrid
613+
weeks={activityWeeks}
614+
mode={activityMode}
615+
usageLabel={tokenUsage.label}
616+
/>
530617
</div>
531-
</div>
618+
</>
532619
) : (
533620
<p className="text-xs text-muted-foreground">No token activity has been reported.</p>
534621
)}

apps/web/src/components/chat/ModelListRow.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,6 @@ export const ModelListRow = memo(function ModelListRow(props: {
7474
data-model-picker-model-name
7575
>
7676
<span className="truncate">{modelLabel}</span>
77-
{props.model.isDefault === true ? (
78-
<span className="shrink-0 font-mono text-[9px] font-normal tracking-[0.08em] text-muted-foreground/65 uppercase">
79-
Default
80-
</span>
81-
) : null}
8277
{/* Inline selection check (no left gutter — rows keep their
8378
full width and unselected rows don't carry an empty column). */}
8479
<CheckIcon

apps/web/src/components/chat/ProviderModelPicker.browser.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ describe("ProviderModelPicker", () => {
577577
}
578578
});
579579

580-
it("marks the model advertised as the provider default", async () => {
580+
it("does not badge the model advertised as the provider default", async () => {
581581
const providers: ReadonlyArray<ServerProvider> = [
582582
{
583583
...TEST_PROVIDERS[0]!,
@@ -609,12 +609,8 @@ describe("ProviderModelPicker", () => {
609609
await openModelPicker();
610610

611611
await vi.waitFor(() => {
612-
const defaultRowText = Array.from(
613-
document.querySelectorAll<HTMLElement>("[data-model-picker-model-name]"),
614-
)
615-
.map((row) => row.textContent ?? "")
616-
.find((text) => text.includes("Default"));
617-
expect(defaultRowText).toContain("Sol");
612+
expect(getVisibleModelNames()).toEqual(["GPT-5.6 Sol", "GPT-5.6 Terra"]);
613+
expect(document.body.textContent).not.toContain("Default");
618614
});
619615
} finally {
620616
await mounted.cleanup();

apps/web/src/components/settings/AgentInstructionsSettings.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,9 +484,38 @@ export function AgentInstructionsSettingsPanel() {
484484
}
485485
>
486486
{instructionFiles.length > 0 ? (
487-
<div className="grid h-[min(42rem,calc(100dvh-16rem))] min-h-120 grid-rows-[auto_minmax(0,1fr)] lg:grid-cols-[15rem_minmax(0,1fr)] lg:grid-rows-1">
487+
<div className="grid h-[58dvh] min-h-[22rem] max-h-[32rem] grid-rows-[auto_minmax(0,1fr)] sm:h-[min(42rem,calc(100dvh-16rem))] sm:min-h-120 sm:max-h-none lg:grid-cols-[15rem_minmax(0,1fr)] lg:grid-rows-1">
488488
<div className="min-w-0 border-b border-border/60 bg-muted/10 lg:border-b-0 lg:border-r">
489-
<div className="flex gap-1 overflow-x-auto p-2 lg:h-full lg:flex-col lg:overflow-x-hidden lg:overflow-y-auto">
489+
<div className="p-2 sm:hidden">
490+
<Select
491+
value={activeFile ? instructionFileKey(activeFile) : ""}
492+
onValueChange={(value) => {
493+
if (value) setActiveFileKey(value);
494+
}}
495+
>
496+
<SelectTrigger className="w-full" aria-label="Instruction file">
497+
<SelectValue>
498+
{activeFile
499+
? `${instructionFileLabel(activeFile)}${
500+
dirtyFileKeys.has(instructionFileKey(activeFile)) ? " • Edited" : ""
501+
}`
502+
: "Select a file"}
503+
</SelectValue>
504+
</SelectTrigger>
505+
<SelectPopup align="start" alignItemWithTrigger={false}>
506+
{instructionFiles.map((file) => {
507+
const key = instructionFileKey(file);
508+
return (
509+
<SelectItem key={key} hideIndicator value={key}>
510+
{instructionFileLabel(file)}
511+
{dirtyFileKeys.has(key) ? " • Edited" : ""}
512+
</SelectItem>
513+
);
514+
})}
515+
</SelectPopup>
516+
</Select>
517+
</div>
518+
<div className="hidden gap-1 overflow-x-auto p-2 sm:flex lg:h-full lg:flex-col lg:overflow-x-hidden lg:overflow-y-auto">
490519
{instructionFiles.map((file) => {
491520
const key = instructionFileKey(file);
492521
return (

apps/web/src/components/settings/ProviderInstanceCard.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,23 +1279,25 @@ function ProviderDetailsNav(props: {
12791279
readonly onSectionChange: (section: ProviderDetailsSection) => void;
12801280
}) {
12811281
return (
1282-
<div className="inline-flex rounded-md border border-border/70 bg-muted/20 p-0.5">
1283-
{props.sections.map((section) => (
1284-
<button
1285-
key={section}
1286-
type="button"
1287-
className={cn(
1288-
"h-7 cursor-pointer rounded px-2.5 text-xs transition-colors",
1289-
props.activeSection === section
1290-
? "bg-background text-foreground shadow-xs"
1291-
: "text-muted-foreground hover:text-foreground",
1292-
)}
1293-
onClick={() => props.onSectionChange(section)}
1294-
aria-pressed={props.activeSection === section}
1295-
>
1296-
{PROVIDER_DETAILS_SECTION_LABELS[section]}
1297-
</button>
1298-
))}
1282+
<div className="w-full overflow-x-auto">
1283+
<div className="inline-flex min-w-max rounded-md border border-border/70 bg-muted/20 p-0.5">
1284+
{props.sections.map((section) => (
1285+
<button
1286+
key={section}
1287+
type="button"
1288+
className={cn(
1289+
"h-7 cursor-pointer rounded px-2.5 text-xs transition-colors",
1290+
props.activeSection === section
1291+
? "bg-background text-foreground shadow-xs"
1292+
: "text-muted-foreground hover:text-foreground",
1293+
)}
1294+
onClick={() => props.onSectionChange(section)}
1295+
aria-pressed={props.activeSection === section}
1296+
>
1297+
{PROVIDER_DETAILS_SECTION_LABELS[section]}
1298+
</button>
1299+
))}
1300+
</div>
12991301
</div>
13001302
);
13011303
}

0 commit comments

Comments
 (0)