Files
kandev/apps/web/components/task/chat/token-usage-display.tsx
T
João Salavisa dea2a07cfa feat: add context window usage display to task session (#66)
* feat: add context window usage display to task session

- Add TokenUsageDisplay component showing context window usage as a horizontal progress bar
- Add useSessionContextWindow hook with proper Zustand reactivity (subscribe to primitives)
- Add ContextWindowState to store with setContextWindow action
- Backend: propagate context_window data through orchestrator, watcher, and lifecycle events
- WebSocket handler extracts context_window from session.state_changed metadata
- Display positioned on left side of action bar, updates in real-time

* refactor: remove agent stream event WebSocket broadcasting

Agent stream events (message_chunk, tool_call, etc.) are now handled
internally by the lifecycle manager rather than being broadcast to
the frontend. Messages are accumulated and flushed as comments at
step boundaries.
2026-01-21 02:47:20 +00:00

61 lines
1.8 KiB
TypeScript

'use client';
import { Tooltip, TooltipContent, TooltipTrigger } from '@kandev/ui/tooltip';
import { cn } from '@/lib/utils';
import type { ContextWindowEntry } from '@/lib/state/store';
type TokenUsageDisplayProps = {
contextWindow: ContextWindowEntry;
className?: string;
};
function formatNumber(num: number): string {
if (num >= 1_000_000) {
return `${(num / 1_000_000).toFixed(1)}M`;
}
if (num >= 1_000) {
return `${(num / 1_000).toFixed(1)}K`;
}
return num.toLocaleString();
}
function getBarColor(efficiency: number): string {
if (efficiency >= 90) return 'bg-red-500';
if (efficiency >= 75) return 'bg-orange-500';
if (efficiency >= 50) return 'bg-yellow-500';
return 'bg-emerald-500';
}
export function TokenUsageDisplay({ contextWindow, className }: TokenUsageDisplayProps) {
const { size, used, remaining, efficiency } = contextWindow;
if (size === 0) return null;
const usagePercent = Math.min(efficiency, 100);
return (
<Tooltip>
<TooltipTrigger asChild>
<div className={cn('flex items-center gap-1.5 cursor-default', className)}>
<div className="w-16 h-1.5 rounded-full bg-muted/50 overflow-hidden">
<div
className={cn('h-full rounded-full transition-all', getBarColor(efficiency))}
style={{ width: `${usagePercent}%` }}
/>
</div>
<span className="text-[10px] text-muted-foreground tabular-nums">
{efficiency.toFixed(0)}%
</span>
</div>
</TooltipTrigger>
<TooltipContent side="top">
<div className="text-xs space-y-1">
<div className="font-medium">Context: {formatNumber(used)} / {formatNumber(size)}</div>
<div className="text-muted-foreground">{formatNumber(remaining)} remaining</div>
</div>
</TooltipContent>
</Tooltip>
);
}