import React, { RefObject, useState } from 'react'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import CircularProgress from '@mui/material/CircularProgress'; import AttachFileIcon from '@mui/icons-material/AttachFile'; import CommandPicker from '@/app/components/editor/CommandPicker'; import { useElementSelection } from '@/app/components/editor/ElementSelectionContext'; import { SelectedElement } from '@/app/components/editor/ElementSelectionContext'; import { ContextPath } from '@/app/components/editor/DirectoryBrowser'; import { ClaudeTokens } from '@/shared/styles/claudeTokens'; import { TriggerState } from '@/app/components/editor/richEditorUtils'; import { AttachedImage, ForcedToolGroup } from '../types'; import type { WorkflowsRunContext } from '@/shared/state/dashboardLayoutSlice'; import { SendBlock } from '../hooks/useContextFiles'; import { ModelPickerState } from '../hooks/useModelPicker'; import { SendBlockBanner } from './SendBlockBanner'; import { AttachmentChips } from './AttachmentChips'; import { EditorSurface } from './EditorSurface'; import { ChatInputToolbar } from '../toolbar/ChatInputToolbar'; import { ChatInputOverlays } from './ChatInputOverlays'; import { setLastFocusedComposer } from '@/shared/composerFocus'; type ThinkingLevel = 'off' | 'low' | 'medium' | 'high' | 'auto'; interface ModeConf { label: string; icon: React.ReactNode; color: string } interface Props { c: ClaudeTokens; containerRef: RefObject; editorRef: RefObject; generalFileInputRef: RefObject; embedded?: boolean; isDragOver: boolean; isUploading: boolean; handleDragOver: (e: React.DragEvent) => void; handleDragLeave: (e: React.DragEvent) => void; handleDrop: (e: React.DragEvent) => void; editorPicker: TriggerState; setPicker: React.Dispatch>; handlePickerSelect: (item: any) => void; sendBlock: SendBlock; setSendBlock: (v: SendBlock) => void; sessionId?: string; images: AttachedImage[]; setLightboxSrc: (src: string | null) => void; removeImage: (idx: number) => void; contextPaths: ContextPath[]; setContextPaths: React.Dispatch>; copiedPathIdx: number | null; setCopiedPathIdx: React.Dispatch>; pdfSupported: boolean; imageSupported: boolean; forcedTools: ForcedToolGroup[]; setForcedTools: React.Dispatch>; selectedElements: SelectedElement[]; elementSelection: ReturnType; ownerId: string; disabled?: boolean; hasContent: boolean; hasAttachments: boolean; autoRunMode?: boolean; isRunning?: boolean; queueLength: number; modeConf: ModeConf; webSearchOn?: boolean; onToggleWebSearch?: () => void; placeholderOverride?: string; ghostSuggestion?: string; runContext?: WorkflowsRunContext; onClearRunContext?: () => void; handleInput: () => void; handleEditorClick: () => void; handleKeyDown: (e: React.KeyboardEvent) => void; handlePaste: (e: React.ClipboardEvent) => void; modesArr: Array<{ id: string; name: string; icon: string; color: string }>; mode: string; onModeChange: (mode: string) => void; iconMap: Record; modeAnchor: HTMLElement | null; setModeAnchor: (el: HTMLElement | null) => void; modelAnchor: HTMLElement | null; setModelAnchor: (el: HTMLElement | null) => void; thinkingAnchor: HTMLElement | null; setThinkingAnchor: (el: HTMLElement | null) => void; allModelFlat: Array; model: string; onModelChange: (model: string) => void; onProviderChange?: (provider: string) => void; picker: ModelPickerState; pendingKinds: Set; pendingPayloadEstimate: number; thinkingLevel: ThinkingLevel; onThinkingLevelChange?: (level: ThinkingLevel) => void; contextEstimate?: { used: number; limit: number }; addImageFiles: (files: FileList | File[]) => void; uploadAndAttachFiles: (files: File[]) => void; onStop?: () => void; handleSend: () => void; lightboxSrc: string | null; oversizeQueue: Array<{ path: string; name: string; tokens: number }>; summarizingPath: string | null; summarizingAll: boolean; summarizeOversize: (path: string) => void; summarizeAllOversize: () => void; detachOversize: (path: string) => void; detachAllOversize: () => void; currentModelCtx: number; summarizeError: string | null; setSummarizeError: (v: string | null) => void; } export const ChatInputView: React.FC = (p) => { const { c } = p; return ( setLastFocusedComposer(p.sessionId ?? 'dashboard')} onDragOver={p.handleDragOver} onDragLeave={p.handleDragLeave} onDrop={p.handleDrop} sx={{ position: 'relative', ...(p.embedded ? {} : { mx: 1.5, mb: 1.5, borderRadius: '16px', border: p.isDragOver ? `1px solid ${c.accent.primary}` : `1px solid ${c.border.subtle}`, bgcolor: c.bg.surface, transition: 'border-color 0.15s', }), }} > {p.isDragOver && ( Drop files here )} {p.isUploading && ( Attaching files… )} p.setPicker((prev) => ({ ...prev, visible: false }))} visible={p.editorPicker.visible} /> {p.sendBlock && ( )} {p.runContext && ( Run attached · {p.runContext.title} )} p.handlePickerSelect({ id: skillId, type: 'skill', category: '', name: '', description: '', command: '', icon: null })} /> ); };