mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-04-12 00:21:30 +00:00
133 lines
5.2 KiB
TypeScript
133 lines
5.2 KiB
TypeScript
import React from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import type { PermissionMode, Provider } from '../../types/types';
|
|
import ThinkingModeSelector from './ThinkingModeSelector';
|
|
|
|
interface ChatInputControlsProps {
|
|
permissionMode: PermissionMode | string;
|
|
onModeSwitch: () => void;
|
|
provider: Provider | string;
|
|
thinkingMode: string;
|
|
setThinkingMode: React.Dispatch<React.SetStateAction<string>>;
|
|
slashCommandsCount: number;
|
|
onToggleCommandMenu: () => void;
|
|
hasInput: boolean;
|
|
onClearInput: () => void;
|
|
isUserScrolledUp: boolean;
|
|
hasMessages: boolean;
|
|
onScrollToBottom: () => void;
|
|
}
|
|
|
|
export default function ChatInputControls({
|
|
permissionMode,
|
|
onModeSwitch,
|
|
provider,
|
|
thinkingMode,
|
|
setThinkingMode,
|
|
slashCommandsCount,
|
|
onToggleCommandMenu,
|
|
hasInput,
|
|
onClearInput,
|
|
isUserScrolledUp,
|
|
hasMessages,
|
|
onScrollToBottom,
|
|
}: ChatInputControlsProps) {
|
|
const { t } = useTranslation('chat');
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center justify-center gap-2 sm:gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={onModeSwitch}
|
|
className={`rounded-lg border px-2.5 py-1 text-sm font-medium transition-all duration-200 sm:px-3 sm:py-1.5 ${
|
|
permissionMode === 'default'
|
|
? 'border-border/60 bg-muted/50 text-muted-foreground hover:bg-muted'
|
|
: permissionMode === 'acceptEdits'
|
|
? 'border-green-300/60 bg-green-50 text-green-700 hover:bg-green-100 dark:border-green-600/40 dark:bg-green-900/15 dark:text-green-300 dark:hover:bg-green-900/25'
|
|
: permissionMode === 'bypassPermissions'
|
|
? 'border-orange-300/60 bg-orange-50 text-orange-700 hover:bg-orange-100 dark:border-orange-600/40 dark:bg-orange-900/15 dark:text-orange-300 dark:hover:bg-orange-900/25'
|
|
: 'border-primary/20 bg-primary/5 text-primary hover:bg-primary/10'
|
|
}`}
|
|
title={t('input.clickToChangeMode')}
|
|
>
|
|
<div className="flex items-center gap-1.5">
|
|
<div
|
|
className={`h-1.5 w-1.5 rounded-full ${
|
|
permissionMode === 'default'
|
|
? 'bg-muted-foreground'
|
|
: permissionMode === 'acceptEdits'
|
|
? 'bg-green-500'
|
|
: permissionMode === 'bypassPermissions'
|
|
? 'bg-orange-500'
|
|
: 'bg-primary'
|
|
}`}
|
|
/>
|
|
<span>
|
|
{permissionMode === 'default' && t('codex.modes.default')}
|
|
{permissionMode === 'acceptEdits' && t('codex.modes.acceptEdits')}
|
|
{permissionMode === 'bypassPermissions' && t('codex.modes.bypassPermissions')}
|
|
{permissionMode === 'plan' && t('codex.modes.plan')}
|
|
</span>
|
|
</div>
|
|
</button>
|
|
|
|
{provider === 'claude' && (
|
|
<ThinkingModeSelector selectedMode={thinkingMode} onModeChange={setThinkingMode} onClose={() => {}} className="" />
|
|
)}
|
|
|
|
<button
|
|
type="button"
|
|
onClick={onToggleCommandMenu}
|
|
className="relative flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground sm:h-8 sm:w-8"
|
|
title={t('input.showAllCommands')}
|
|
>
|
|
<svg className="h-4 w-4 sm:h-5 sm:w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"
|
|
/>
|
|
</svg>
|
|
{slashCommandsCount > 0 && (
|
|
<span
|
|
className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-[10px] font-bold text-primary-foreground sm:h-5 sm:w-5"
|
|
>
|
|
{slashCommandsCount}
|
|
</span>
|
|
)}
|
|
</button>
|
|
|
|
{hasInput && (
|
|
<button
|
|
type="button"
|
|
onClick={onClearInput}
|
|
className="group flex h-7 w-7 items-center justify-center rounded-lg border border-border/50 bg-card shadow-sm transition-all duration-200 hover:bg-accent/60 sm:h-8 sm:w-8"
|
|
title={t('input.clearInput', { defaultValue: 'Clear input' })}
|
|
>
|
|
<svg
|
|
className="h-3.5 w-3.5 text-muted-foreground transition-colors group-hover:text-foreground sm:h-4 sm:w-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
)}
|
|
|
|
{isUserScrolledUp && hasMessages && (
|
|
<button
|
|
onClick={onScrollToBottom}
|
|
className="flex h-7 w-7 items-center justify-center rounded-lg bg-primary text-primary-foreground shadow-sm transition-all duration-200 hover:scale-105 hover:bg-primary/90 sm:h-8 sm:w-8"
|
|
title={t('input.scrollToBottom', { defaultValue: 'Scroll to bottom' })}
|
|
>
|
|
<svg className="h-3.5 w-3.5 sm:h-4 sm:w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
|
</svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|