mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-18 06:37:33 +00:00
feat(chat): move thinking modes, token usage pie, and related logic into chat folder
This commit is contained in:
44
src/components/chat/constants/thinkingModes.ts
Normal file
44
src/components/chat/constants/thinkingModes.ts
Normal 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'
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -11,7 +11,9 @@ import type {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { useDropzone } from 'react-dropzone';
|
import { useDropzone } from 'react-dropzone';
|
||||||
import { authenticatedFetch } from '../../../utils/api';
|
import { authenticatedFetch } from '../../../utils/api';
|
||||||
import { thinkingModes } from '../../ThinkingModeSelector.jsx';
|
|
||||||
|
import { thinkingModes } from '../constants/thinkingModes';
|
||||||
|
|
||||||
import { grantClaudeToolPermission } from '../utils/chatPermissions';
|
import { grantClaudeToolPermission } from '../utils/chatPermissions';
|
||||||
import { safeLocalStorage } from '../utils/chatStorage';
|
import { safeLocalStorage } from '../utils/chatStorage';
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ThinkingModeSelector from '../../ThinkingModeSelector.jsx';
|
import ThinkingModeSelector from './ThinkingModeSelector.jsx';
|
||||||
import TokenUsagePie from '../../TokenUsagePie';
|
import TokenUsagePie from './TokenUsagePie';
|
||||||
import type { PermissionMode, Provider } from '../types';
|
import type { PermissionMode, Provider } from '../../types/types';
|
||||||
|
|
||||||
interface ChatInputControlsProps {
|
interface ChatInputControlsProps {
|
||||||
permissionMode: PermissionMode | string;
|
permissionMode: PermissionMode | string;
|
||||||
|
|||||||
@@ -1,55 +1,21 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { Brain, Zap, Sparkles, Atom, X } from 'lucide-react';
|
import { Brain, X } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const thinkingModes = [
|
import { thinkingModes } from '../../constants/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'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
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');
|
const { t } = useTranslation('chat');
|
||||||
|
|
||||||
// Mapping from mode ID to translation key
|
// Mapping from mode ID to translation key
|
||||||
const modeKeyMap = {
|
const modeKeyMap: Record<string, string> = {
|
||||||
'think-hard': 'thinkHard',
|
'think-hard': 'thinkHard',
|
||||||
'think-harder': 'thinkHarder'
|
'think-harder': 'thinkHarder'
|
||||||
};
|
};
|
||||||
@@ -65,11 +31,11 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const dropdownRef = useRef(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
if (onClose) onClose();
|
if (onClose) onClose();
|
||||||
}
|
}
|
||||||
@@ -87,11 +53,10 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
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 ${
|
className={`w-10 h-10 sm:w-10 sm:h-10 rounded-full flex items-center justify-center transition-all duration-200 ${selectedMode === 'none'
|
||||||
selectedMode === 'none'
|
|
||||||
? 'bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600'
|
? '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'
|
: 'bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800'
|
||||||
}`}
|
}`}
|
||||||
title={t('thinkingMode.buttonTitle', { mode: currentMode.name })}
|
title={t('thinkingMode.buttonTitle', { mode: currentMode.name })}
|
||||||
>
|
>
|
||||||
<IconComponent className={`w-5 h-5 ${currentMode.color}`} />
|
<IconComponent className={`w-5 h-5 ${currentMode.color}`} />
|
||||||
@@ -132,9 +97,8 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
|
|||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
if (onClose) onClose();
|
if (onClose) onClose();
|
||||||
}}
|
}}
|
||||||
className={`w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors ${
|
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' : ''
|
||||||
isSelected ? 'bg-gray-50 dark:bg-gray-700' : ''
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className={`mt-0.5 ${mode.icon ? mode.color : 'text-gray-400'}`}>
|
<div className={`mt-0.5 ${mode.icon ? mode.color : 'text-gray-400'}`}>
|
||||||
@@ -142,9 +106,8 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className={`font-medium text-sm ${
|
<span className={`font-medium text-sm ${isSelected ? 'text-gray-900 dark:text-white' : 'text-gray-700 dark:text-gray-300'
|
||||||
isSelected ? 'text-gray-900 dark:text-white' : 'text-gray-700 dark:text-gray-300'
|
}`}>
|
||||||
}`}>
|
|
||||||
{mode.name}
|
{mode.name}
|
||||||
</span>
|
</span>
|
||||||
{isSelected && (
|
{isSelected && (
|
||||||
@@ -180,4 +143,3 @@ function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ThinkingModeSelector;
|
export default ThinkingModeSelector;
|
||||||
export { thinkingModes };
|
|
||||||
@@ -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
|
// Token usage visualization component
|
||||||
// Only bail out on missing values or non‐positive totals; allow used===0 to render 0%
|
// Only bail out on missing values or non‐positive 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 percentage = Math.min(100, (used / total) * 100);
|
||||||
const radius = 10;
|
const radius = 10;
|
||||||
@@ -49,5 +52,3 @@ function TokenUsagePie({ used, total }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TokenUsagePie;
|
|
||||||
Reference in New Issue
Block a user