diff --git a/client/src/components/Chat/Input/TokenUsageIndicator.tsx b/client/src/components/Chat/Input/TokenUsageIndicator.tsx
index d99c1bcf94..a70f356d03 100644
--- a/client/src/components/Chat/Input/TokenUsageIndicator.tsx
+++ b/client/src/components/Chat/Input/TokenUsageIndicator.tsx
@@ -1,5 +1,5 @@
import { memo } from 'react';
-import { TooltipAnchor } from '@librechat/client';
+import { HoverCard, HoverCardTrigger, HoverCardContent, HoverCardPortal } from '@librechat/client';
import { useLocalize, useTokenUsage } from '~/hooks';
import { cn } from '~/utils';
@@ -13,6 +13,147 @@ function formatTokens(n: number): string {
return n.toString();
}
+interface ProgressBarProps {
+ value: number;
+ max: number;
+ colorClass: string;
+ showPercentage?: boolean;
+}
+
+function ProgressBar({ value, max, colorClass, showPercentage = false }: ProgressBarProps) {
+ const percentage = max > 0 ? Math.min((value / max) * 100, 100) : 0;
+
+ return (
+
+
+ {showPercentage && (
+
+ {Math.round(percentage)}%
+
+ )}
+
+ );
+}
+
+interface TokenRowProps {
+ label: string;
+ value: number;
+ total: number;
+ colorClass: string;
+}
+
+function TokenRow({ label, value, total, colorClass }: TokenRowProps) {
+ const percentage = total > 0 ? Math.round((value / total) * 100) : 0;
+
+ return (
+
+
+ {label}
+
+ {formatTokens(value)}
+ ({percentage}%)
+
+
+
+
+ );
+}
+
+function TokenUsageContent() {
+ const localize = useLocalize();
+ const { inputTokens, outputTokens, maxContext } = useTokenUsage();
+
+ const totalUsed = inputTokens + outputTokens;
+ const hasMaxContext = maxContext !== null && maxContext > 0;
+ const percentage = hasMaxContext ? Math.min((totalUsed / maxContext) * 100, 100) : 0;
+
+ const getMainProgressColor = () => {
+ if (!hasMaxContext) {
+ return 'bg-text-secondary';
+ }
+ if (percentage > 90) {
+ return 'bg-red-500';
+ }
+ if (percentage > 75) {
+ return 'bg-yellow-500';
+ }
+ return 'bg-green-500';
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ {localize('com_ui_token_usage_context')}
+
+ {hasMaxContext && (
+ 90,
+ 'text-yellow-500': percentage > 75 && percentage <= 90,
+ 'text-green-500': percentage <= 75,
+ })}
+ >
+ {localize('com_ui_token_usage_percent', { 0: Math.round(percentage).toString() })}
+
+ )}
+
+
+ {/* Main Progress Bar */}
+ {hasMaxContext && (
+
+
+
+ {formatTokens(totalUsed)}
+ {formatTokens(maxContext)}
+
+
+ )}
+
+ {/* Divider */}
+
+
+ {/* Input/Output Breakdown */}
+
+
+
+
+
+ {/* Total Section */}
+
+
+ {localize('com_ui_token_usage_total')}
+ {formatTokens(totalUsed)}
+
+
+
+ {/* Max Context (when available) */}
+ {hasMaxContext && (
+
+ {localize('com_ui_token_usage_max_context')}
+ {formatTokens(maxContext)}
+
+ )}
+
+ );
+}
+
const TokenUsageIndicator = memo(function TokenUsageIndicator() {
const localize = useLocalize();
const { inputTokens, outputTokens, maxContext } = useTokenUsage();
@@ -28,17 +169,6 @@ const TokenUsageIndicator = memo(function TokenUsageIndicator() {
const circumference = 2 * Math.PI * radius;
const offset = circumference - (percentage / 100) * circumference;
- const tooltipText = hasMaxContext
- ? localize('com_ui_token_usage_with_max', {
- 0: formatTokens(inputTokens),
- 1: formatTokens(outputTokens),
- 2: formatTokens(maxContext),
- })
- : localize('com_ui_token_usage_no_max', {
- 0: formatTokens(inputTokens),
- 1: formatTokens(outputTokens),
- });
-
const ariaLabel = hasMaxContext
? localize('com_ui_token_usage_aria_full', {
0: formatTokens(inputTokens),
@@ -66,12 +196,11 @@ const TokenUsageIndicator = memo(function TokenUsageIndicator() {
};
return (
-
+
+
+
+
+
+
+
+
+
);
});
diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json
index 9a15b24253..4efe902309 100644
--- a/client/src/locales/en/translation.json
+++ b/client/src/locales/en/translation.json
@@ -1324,8 +1324,14 @@
"com_ui_token_url": "Token URL",
"com_ui_token_usage_aria_full": "Token usage: {{0}} input, {{1}} output, {{2}} max context, {{3}}% used",
"com_ui_token_usage_aria_no_max": "Token usage: {{0}} input, {{1}} output",
+ "com_ui_token_usage_context": "Context Usage",
"com_ui_token_usage_indicator": "Token usage indicator",
+ "com_ui_token_usage_input": "Input",
+ "com_ui_token_usage_max_context": "Max Context",
"com_ui_token_usage_no_max": "Input: {{0}} | Output: {{1}} | Max: N/A",
+ "com_ui_token_usage_output": "Output",
+ "com_ui_token_usage_percent": "{{0}}% used",
+ "com_ui_token_usage_total": "Total",
"com_ui_token_usage_with_max": "Input: {{0}} | Output: {{1}} | Max: {{2}}",
"com_ui_tokens": "tokens",
"com_ui_tool_collection_prefix": "A collection of tools from",