83 / 122 · Composer
Context breakdown
Where the window actually went: prompt, tools, files, conversation, and what's left.
Context50,805 / 128,000
System prompt810
Tools1,890
Attached files17,325
Conversation30,780
Headroom77,195
Installation
npx shadcn@latest add "@assistant-ui/elements-context-breakdown"
Usage
import { ContextBreakdown } from "@/components/elements/context-breakdown";
<ContextBreakdown segments={segments} limit={128000} />Props
ContextBreakdown
segments*ContextSegment[]What is occupying the window, in stacking order. Headroom is derived, not passed.
limit*numberWindow size. The header turns amber once the segments pass 85 percent of it. A zero limit is treated as zero pressure rather than dividing by it.
classNamestringExtra classes merged onto the root.
ContextSegment
label*stringWhat this slice of the window is, shown in the legend.
tokens*numberRaw token count. The element formats it.
tint*stringBackground class shared by the bar slice and the legend dot, so the two always agree.
Source
context-breakdown.tsx"use client";
import { cn } from "@/lib/utils";
import { mono, paper } from "./surfaces";
const fmt = (n: number) => n.toLocaleString("en-US");
export interface ContextSegment {
label: string;
tokens: number;
tint: string;
}
export function ContextBreakdown({
segments,
limit,
className,
}: {
segments: readonly ContextSegment[];
limit: number;
className?: string;
}) {
const used = segments.reduce((sum, segment) => sum + segment.tokens, 0);
const pressure = limit === 0 ? 0 : used / limit;
const share = (tokens: number) => (limit === 0 ? 0 : (tokens / limit) * 100);
return (
<div
className={cn(
paper,
"flex w-full max-w-sm flex-col gap-3 rounded-2xl p-4",
className,
)}
>
<div className="flex items-baseline justify-between">
<span className="text-[13.5px] font-medium">Context</span>
<span
className={cn(
mono,
"tabular-nums",
pressure > 0.85
? "text-amber-600 dark:text-amber-400"
: "text-foreground/35",
)}
>
{fmt(used)} / {fmt(limit)}
</span>
</div>
<div className="bg-foreground/[0.06] flex h-2 w-full overflow-hidden rounded-full">
{segments.map((segment) => (
<span
key={segment.label}
className={cn(
"h-full transition-[width] duration-500 ease-out motion-reduce:transition-none",
segment.tint,
)}
style={{ width: `${share(segment.tokens)}%` }}
/>
))}
</div>
<div className="flex flex-col gap-1.5">
{segments.map((segment) => (
<div key={segment.label} className="flex items-center gap-2">
<span
aria-hidden
className={cn("size-2 shrink-0 rounded-full", segment.tint)}
/>
<span className="text-foreground/70 min-w-0 flex-1 truncate text-[13px]">
{segment.label}
</span>
<span
className={cn(mono, "text-foreground/35 shrink-0 tabular-nums")}
>
{fmt(segment.tokens)}
</span>
</div>
))}
<div className="flex items-center gap-2">
<span
aria-hidden
className="bg-foreground/[0.08] size-2 shrink-0 rounded-full"
/>
<span className="text-foreground/35 min-w-0 flex-1 truncate text-[13px]">
Headroom
</span>
<span
className={cn(mono, "text-foreground/25 shrink-0 tabular-nums")}
>
{fmt(Math.max(0, limit - used))}
</span>
</div>
</div>
</div>
);
}