diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 1d6c6125..cd14a094 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -21,6 +21,7 @@ import { toastError } from '../toasts'; import MentionPopover, { DisplayItemWithMatch } from './MentionPopover'; import { COST_TRACKING_ENABLED } from '../updates'; import { CostTracker } from './bottom_menu/CostTracker'; +import { ContextWindowIndicator } from './bottom_menu/ContextWindowIndicator'; import { DroppedFile, useFileDrop } from '../hooks/useFileDrop'; import { Recipe } from '../recipe'; import { MessageQueue, QueuedMessage } from './MessageQueue'; @@ -1628,13 +1629,17 @@ export default function ChatInput({ )} +
= { [AlertType.Info]: 'dark:bg-white dark:text-black bg-black text-white', }; -const formatTokenCount = (count: number): string => { - if (count >= 1000000) { - const millions = count / 1000000; - return millions % 1 === 0 ? `${millions.toFixed(0)}M` : `${millions.toFixed(1)}M`; - } else if (count >= 1000) { - const thousands = count / 1000; - return thousands % 1 === 0 ? `${thousands.toFixed(0)}k` : `${thousands.toFixed(1)}k`; - } - return count.toString(); -}; - export const AlertBox = ({ alert, className }: AlertBoxProps) => { const intl = useIntl(); const { read } = useConfig(); @@ -125,8 +114,6 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => { > {alert.progress ? (
- {alert.message} - {/* Auto-compact threshold indicator with edit */}
{isEditingThreshold ? ( @@ -140,16 +127,13 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => { value={thresholdValue} onChange={(e) => { const val = parseInt(e.target.value, 10); - // Allow empty input for easier editing if (e.target.value === '') { setThresholdValue(1); } else if (!isNaN(val)) { - // Clamp value between 1 and 100 setThresholdValue(Math.max(1, Math.min(100, val))); } }} onBlur={(e) => { - // On blur, ensure we have a valid value const val = parseInt(e.target.value, 10); if (isNaN(val) || val < 1) { setThresholdValue(1); @@ -167,11 +151,9 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => { } }} onFocus={(e) => { - // Select all text on focus for easier editing e.target.select(); }} onClick={(e) => { - // Prevent issues with text selection e.stopPropagation(); }} className="w-12 px-1 text-[10px] bg-white/10 border border-current/30 rounded outline-none text-center focus:bg-white/20 focus:border-current/50 transition-colors" @@ -213,72 +195,6 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => { )}
- -
- {(() => { - let closestDotIndex = -1; - if (currentThreshold !== undefined && currentThreshold > 0 && currentThreshold <= 1) { - let minDistance = Infinity; - for (let j = 0; j < 30; j++) { - const dotPos = j / 29; - const distance = Math.abs(dotPos - currentThreshold); - if (distance < minDistance) { - minDistance = distance; - closestDotIndex = j; - } - } - } - - return [...Array(30)].map((_, i) => { - const progress = alert.progress!.current / alert.progress!.total; - const progressPercentage = Math.round(progress * 100); - const dotPosition = i / 29; // 0 to 1 range for 30 dots - const isActive = dotPosition <= progress; - const isThresholdDot = i === closestDotIndex; - - const getProgressColor = () => { - if (progressPercentage <= 50) { - return 'bg-green-500'; - } else if (progressPercentage <= 75) { - return 'bg-yellow-500'; - } else if (progressPercentage <= 90) { - return 'bg-orange-500'; - } else { - return 'bg-red-500'; - } - }; - - const progressColor = getProgressColor(); - const inactiveColor = 'bg-gray-300 dark:bg-gray-600'; - - return ( -
- ); - }); - })()} -
-
-
- - {formatTokenCount(alert.progress!.current)} - - - {Math.round((alert.progress!.current / alert.progress!.total) * 100)}% - -
- - {formatTokenCount(alert.progress!.total)} - -
{alert.showCompactButton && alert.onCompact && (
diff --git a/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx b/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx new file mode 100644 index 00000000..1e74de09 --- /dev/null +++ b/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx @@ -0,0 +1,49 @@ +import BottomMenuAlertPopover from './BottomMenuAlertPopover'; +import { Alert } from '../alerts'; + +interface ContextWindowIndicatorProps { + totalTokens: number; + tokenLimit: number; + alerts: Alert[]; +} + +const formatTokenCount = (count: number): string => { + if (count >= 1000000) { + const millions = count / 1000000; + return millions % 1 === 0 ? `${millions.toFixed(0)}M` : `${millions.toFixed(1)}M`; + } else if (count >= 1000) { + const thousands = count / 1000; + return thousands % 1 === 0 ? `${thousands.toFixed(0)}k` : `${thousands.toFixed(1)}k`; + } + return count.toString(); +}; + +const getProgressColor = (percentage: number): string => { + if (percentage <= 75) return 'text-text-primary/70'; + if (percentage <= 90) return 'text-orange-500'; + return 'text-red-500'; +}; + +export function ContextWindowIndicator({ + totalTokens, + tokenLimit, + alerts, +}: ContextWindowIndicatorProps) { + if (!tokenLimit) return null; + + const percentage = Math.round((totalTokens / tokenLimit) * 100); + const colorClass = getProgressColor(percentage); + + return ( + <> +
+ + + {formatTokenCount(totalTokens)} / {formatTokenCount(tokenLimit)} + + +
+
+ + ); +} diff --git a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx index 959eba07..e818c64e 100644 --- a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx +++ b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx @@ -12,8 +12,7 @@ import { import { useConfig } from '../../../ConfigContext'; import { getProviderMetadata } from '../modelInterface'; import { getModelDisplayName } from '../predefinedModelsUtils'; -import { Alert } from '../../../alerts'; -import BottomMenuAlertPopover from '../../../bottom_menu/BottomMenuAlertPopover'; + import { ModelSettingsPanel } from '../../localInference/ModelSettingsPanel'; import { ScrollArea } from '../../../ui/scroll-area'; import { defineMessages, useIntl } from '../../../../i18n'; @@ -45,7 +44,6 @@ interface ModelsBottomBarProps { sessionId: string | null; dropdownRef: React.RefObject; setView: (view: View) => void; - alerts: Alert[]; sessionModel?: string | null; sessionProvider?: string | null; onModelChanged: (override: { model: string; provider: string }) => void; @@ -56,7 +54,6 @@ export default function ModelsBottomBar({ sessionId, dropdownRef, setView, - alerts, sessionModel, sessionProvider, onModelChanged, @@ -120,7 +117,6 @@ export default function ModelsBottomBar({ return (
-