feat(chat): move thinking modes, token usage pie, and related logic into chat folder

This commit is contained in:
Haileyesus
2026-02-11 18:58:06 +03:00
parent 189b1533b2
commit 720a771b2a
5 changed files with 80 additions and 71 deletions

View File

@@ -0,0 +1,44 @@
import { Brain, Zap, Sparkles, Atom } from 'lucide-react';
export const thinkingModes = [
{
id: 'none',
name: 'Standard',
description: 'Regular Claude response',
icon: null,
prefix: '',
color: 'text-gray-600'
},
{
id: 'think',
name: 'Think',
description: 'Basic extended thinking',
icon: Brain,
prefix: 'think',
color: 'text-blue-600'
},
{
id: 'think-hard',
name: 'Think Hard',
description: 'More thorough evaluation',
icon: Zap,
prefix: 'think hard',
color: 'text-purple-600'
},
{
id: 'think-harder',
name: 'Think Harder',
description: 'Deep analysis with alternatives',
icon: Sparkles,
prefix: 'think harder',
color: 'text-indigo-600'
},
{
id: 'ultrathink',
name: 'Ultrathink',
description: 'Maximum thinking budget',
icon: Atom,
prefix: 'ultrathink',
color: 'text-red-600'
}
];

View File

@@ -11,7 +11,9 @@ import type {
} from 'react';
import { useDropzone } from 'react-dropzone';
import { authenticatedFetch } from '../../../utils/api';
import { thinkingModes } from '../../ThinkingModeSelector.jsx';
import { thinkingModes } from '../constants/thinkingModes';
import { grantClaudeToolPermission } from '../utils/chatPermissions';
import { safeLocalStorage } from '../utils/chatStorage';
import type {

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import ThinkingModeSelector from '../../ThinkingModeSelector.jsx';
import TokenUsagePie from '../../TokenUsagePie';
import type { PermissionMode, Provider } from '../types';
import ThinkingModeSelector from './ThinkingModeSelector.jsx';
import TokenUsagePie from './TokenUsagePie';
import type { PermissionMode, Provider } from '../../types/types';
interface ChatInputControlsProps {
permissionMode: PermissionMode | string;

View File

@@ -1,55 +1,21 @@
import React, { useState, useRef, useEffect } from 'react';
import { Brain, Zap, Sparkles, Atom, X } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
import { Brain, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
const thinkingModes = [
{
id: 'none',
name: 'Standard',
description: 'Regular Claude response',
icon: null,
prefix: '',
color: 'text-gray-600'
},
{
id: 'think',
name: 'Think',
description: 'Basic extended thinking',
icon: Brain,
prefix: 'think',
color: 'text-blue-600'
},
{
id: 'think-hard',
name: 'Think Hard',
description: 'More thorough evaluation',
icon: Zap,
prefix: 'think hard',
color: 'text-purple-600'
},
{
id: 'think-harder',
name: 'Think Harder',
description: 'Deep analysis with alternatives',
icon: Sparkles,
prefix: 'think harder',
color: 'text-indigo-600'
},
{
id: 'ultrathink',
name: 'Ultrathink',
description: 'Maximum thinking budget',
icon: Atom,
prefix: 'ultrathink',
color: 'text-red-600'
}
];
import { thinkingModes } from '../../constants/thinkingModes';
function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className = '' }) {
type ThinkingModeSelectorProps = {
selectedMode: string;
onModeChange: (modeId: string) => void;
onClose?: () => void;
className?: string;
};
function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className = '' }: ThinkingModeSelectorProps) {
const { t } = useTranslation('chat');
// Mapping from mode ID to translation key
const modeKeyMap = {
const modeKeyMap: Record<string, string> = {
'think-hard': 'thinkHard',
'think-harder': 'thinkHarder'
};
@@ -65,11 +31,11 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
});
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
if (onClose) onClose();
}
@@ -87,11 +53,10 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className={`w-10 h-10 sm:w-10 sm:h-10 rounded-full flex items-center justify-center transition-all duration-200 ${
selectedMode === 'none'
className={`w-10 h-10 sm:w-10 sm:h-10 rounded-full flex items-center justify-center transition-all duration-200 ${selectedMode === 'none'
? 'bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600'
: 'bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800'
}`}
}`}
title={t('thinkingMode.buttonTitle', { mode: currentMode.name })}
>
<IconComponent className={`w-5 h-5 ${currentMode.color}`} />
@@ -123,7 +88,7 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
{translatedModes.map((mode) => {
const ModeIcon = mode.icon;
const isSelected = mode.id === selectedMode;
return (
<button
key={mode.id}
@@ -132,9 +97,8 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
setIsOpen(false);
if (onClose) onClose();
}}
className={`w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors ${
isSelected ? 'bg-gray-50 dark:bg-gray-700' : ''
}`}
className={`w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors ${isSelected ? 'bg-gray-50 dark:bg-gray-700' : ''
}`}
>
<div className="flex items-start gap-3">
<div className={`mt-0.5 ${mode.icon ? mode.color : 'text-gray-400'}`}>
@@ -142,9 +106,8 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className={`font-medium text-sm ${
isSelected ? 'text-gray-900 dark:text-white' : 'text-gray-700 dark:text-gray-300'
}`}>
<span className={`font-medium text-sm ${isSelected ? 'text-gray-900 dark:text-white' : 'text-gray-700 dark:text-gray-300'
}`}>
{mode.name}
</span>
{isSelected && (
@@ -179,5 +142,4 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
);
}
export default ThinkingModeSelector;
export { thinkingModes };
export default ThinkingModeSelector;

View File

@@ -1,9 +1,12 @@
import React from 'react';
type TokenUsagePieProps = {
used: number;
total: number;
};
function TokenUsagePie({ used, total }) {
export default function TokenUsagePie({ used, total }: TokenUsagePieProps) {
// Token usage visualization component
// Only bail out on missing values or nonpositive totals; allow used===0 to render 0%
if (used == null || total == null || total <= 0) return null;
if (used == null || total == null || total <= 0) return null;
const percentage = Math.min(100, (used / total) * 100);
const radius = 10;
@@ -48,6 +51,4 @@ function TokenUsagePie({ used, total }) {
</span>
</div>
);
}
export default TokenUsagePie;
}