Adds a floating popover above the chat input showing current context-window usage. Modeled on Paseo's tracker. - New hook useChatContextStats(chatId, messages) finds the latest assistant message in the chat with both ctx_used and ctx_max set, computes percent, and returns null when data unavailable. - New component ChatContextPopover renders a small card with the "Context window" label, big percent, and "used / max tokens" subline. Hidden when stats is null. - Color thresholds: <60% muted, 60-85 amber, >85 destructive. - Not a portal — absolutely positioned inside a new relative wrapper around ChatInput in ChatPane.tsx, so it's pane-local (multi-pane safe). - Live updates via the existing messages-array dependency. - No API / schema / WS changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
import { useMemo } from 'react';
|
|
import type { Message } from '@/api/types';
|
|
|
|
export interface ChatContextStats {
|
|
used: number;
|
|
max: number;
|
|
percent: number;
|
|
}
|
|
|
|
/**
|
|
* Returns the latest context-window usage for the given chat, derived from the
|
|
* assistant message (with both ctx_used and ctx_max populated) having the most
|
|
* recent created_at. Returns null when no such message exists.
|
|
*
|
|
* Re-evaluates whenever the `messages` reference or `chatId` changes, which
|
|
* matches the cadence of streaming updates from `useSessionStream`.
|
|
*/
|
|
export function useChatContextStats(
|
|
chatId: string,
|
|
messages: Message[],
|
|
): ChatContextStats | null {
|
|
return useMemo(() => {
|
|
let latest: Message | null = null;
|
|
for (const m of messages) {
|
|
if (m.chat_id !== chatId) continue;
|
|
if (m.role !== 'assistant') continue;
|
|
if (m.ctx_used == null || m.ctx_max == null) continue;
|
|
if (!latest || m.created_at > latest.created_at) latest = m;
|
|
}
|
|
if (!latest || latest.ctx_used == null || latest.ctx_max == null) return null;
|
|
const used = latest.ctx_used;
|
|
const max = latest.ctx_max;
|
|
if (max <= 0) return null;
|
|
const percent = Math.round((used / max) * 100);
|
|
return { used, max, percent };
|
|
}, [chatId, messages]);
|
|
}
|