import React, { useRef, useCallback } from 'react'
import { IoDocumentTextOutline, IoCloseOutline, IoStopOutline } from 'react-icons/io5'
import ModelSelector from '../ModelSelector'
import { UsageInfo } from '@/lib/hooks/useUsageLimit'
import { getBookChatModels } from '@/lib/config/models'
interface ChatInputProps {
// Input state
inputValue: string
setInputValue: (value: string) => void
// Loading/streaming state
isLoading: boolean
currentChat: any
// Model selection
selectedModel: string
onModelChange: (model: string) => void
usageInfo: UsageInfo | null
// File inclusion
bookId?: string
includedFiles: string[]
includedFileNames: Map<string, string>
getDisplayName: (fileIdentifier: string) => string
removeIncludedFile: (fileIdentifier: string) => void
// Drag and drop
isDragOver: boolean
handleDragOver: (e: React.DragEvent) => void
handleDragEnter: (e: React.DragEvent) => void
handleDragLeave: (e: React.DragEvent) => void
handleDrop: (e: React.DragEvent) => void
// Handlers
onSubmit: (e: React.FormEvent) => void
onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
stopExecution: () => void
}
export default function ChatInput({
inputValue,
setInputValue,
isLoading,
currentChat,
selectedModel,
onModelChange,
usageInfo,
bookId,
includedFiles,
includedFileNames,
getDisplayName,
removeIncludedFile,
isDragOver,
handleDragOver,
handleDragEnter,
handleDragLeave,
handleDrop,
onSubmit,
onKeyDown,
stopExecution
}: ChatInputProps) {
const textareaRef = useRef<HTMLTextAreaElement>(null)
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(e.target.value)
// Auto-resize textarea
const target = e.target as HTMLTextAreaElement
target.style.height = 'auto'
target.style.height = `${Math.min(target.scrollHeight, 120)}px`
}, [setInputValue])
const getPlaceholder = () => {
if (isLoading) return "AI is thinking..."
if (!currentChat) return "Create a new chat to start messaging..."
return "What would you like me to search for, update, create, improve, critique, etc.?"
}
return (
<div className="border-t border-slate-700/50 bg-slate-800/40 backdrop-blur-sm px-3 py-2">
<form onSubmit={onSubmit} onClick={(e) => {
if (e.target instanceof HTMLElement && !e.target.closest('button[type="submit"]')) {
e.preventDefault()
}
}}>
{/* Input wrapper with border */}
<div
className={`relative bg-slate-800/50 backdrop-blur-sm border border-slate-700/50 rounded-lg focus-within:ring-1 focus-within:ring-teal-500/50 focus-within:border-transparent transition-all ${
isDragOver ? 'border-teal-500 bg-slate-700/50' : ''
}`}
onDragOver={handleDragOver}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
{/* @ Files Section (above textarea) */}
{(includedFiles.length > 0 || bookId) && (
<div className="px-3 pt-2 pb-1.5 border-b border-slate-700/50">
<div className="flex items-center gap-1.5 text-[10px] text-slate-400">
<span className="text-slate-300">@</span>
<div className="flex flex-wrap gap-1">
{bookId && (
<span className="bg-gradient-to-r from-teal-500/20 to-cyan-500/20 px-1.5 py-0.5 rounded text-[10px] text-slate-300 flex items-center gap-1 border border-teal-500/20">
<IoDocumentTextOutline className="w-2.5 h-2" />
Smart Mode
</span>
)}
{includedFiles.map((fileIdentifier, index) => (
<span key={index} className="bg-gradient-to-r from-teal-500 to-cyan-600 px-1.5 py-0.5 rounded text-[10px] text-white flex items-center gap-1">
<IoDocumentTextOutline className="w-2.5 h-2.5" />
{getDisplayName(fileIdentifier)}
<button
type="button"
onClick={() => removeIncludedFile(fileIdentifier)}
className="ml-0.5 hover:bg-teal-700/50 rounded-full p-0.5 transition-colors"
>
<IoCloseOutline className="w-2 h-2" />
</button>
</span>
))}
</div>
</div>
</div>
)}
{/* Drag overlay */}
{isDragOver && (
<div className="absolute inset-0 bg-teal-500/20 border-2 border-dashed border-teal-500 rounded-lg flex items-center justify-center z-10">
<div className="text-teal-300 text-xs font-medium">
Drop files here to include in context
</div>
</div>
)}
{/* Textarea */}
<textarea
ref={textareaRef}
value={inputValue}
onChange={handleInputChange}
onKeyDown={onKeyDown}
placeholder={getPlaceholder()}
className="chat-input w-full px-3 py-2 text-sm bg-transparent border-none focus:outline-none focus:ring-0 focus:border-none resize-none overflow-y-auto text-slate-200 placeholder-slate-400 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-150"
disabled={isLoading || !currentChat}
style={{
height: 'auto',
minHeight: '32px',
maxHeight: '120px',
outline: 'none',
outlineOffset: '0',
outlineStyle: 'none',
outlineWidth: '0'
}}
/>
{/* Bottom section with model picker and send button */}
<div className="flex items-center justify-between px-3 pb-2">
<div className="flex items-center gap-1 text-[10px] text-slate-400">
<ModelSelector
selectedModel={selectedModel}
onModelChange={onModelChange}
disabled={isLoading}
variant="compact"
className="min-w-0"
usageInfo={usageInfo}
models={getBookChatModels()}
/>
{isLoading && (
<div className="w-2 h-2 border border-slate-400 border-t-transparent rounded-full animate-spin ml-1.5"></div>
)}
</div>
<button
type={isLoading ? "button" : "submit"}
onClick={isLoading ? stopExecution : undefined}
className={`px-2.5 py-1 text-[10px] rounded-md transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1 transform hover:scale-105 active:scale-95 ${
isLoading
? 'text-red-400 border border-red-500/50 hover:border-red-500 hover:bg-red-500/10 bg-transparent'
: 'text-white bg-gradient-to-r from-teal-600 to-cyan-600 hover:from-teal-700 hover:to-cyan-700 disabled:hover:from-teal-600 disabled:hover:to-cyan-600 shadow-sm hover:shadow-md'
}`}
disabled={!isLoading && (!inputValue.trim() || !currentChat)}
>
{isLoading ? (
<>
<IoStopOutline className="w-2.5 h-2.5" />
Stop
</>
) : (
'Send'
)}
</button>
</div>
</div>
</form>
</div>
)
}