import { ArrowRight, Check, Funnel, Loader2, Plus } from "lucide-react"; import { forwardRef, useImperativeHandle, useRef, useState } from "react"; import TextareaAutosize from "react-textarea-autosize"; import type { FilterColor } from "@/components/filter-icon-popover"; import { Button } from "@/components/ui/button"; import { Popover, PopoverAnchor, PopoverContent, } from "@/components/ui/popover"; import type { KnowledgeFilterData } from "../_types/types"; import { FilePreview } from "./file-preview"; import { SelectedKnowledgeFilter } from "./selected-knowledge-filter"; export interface ChatInputHandle { focusInput: () => void; clickFileInput: () => void; } interface ChatInputProps { input: string; loading: boolean; isUploading: boolean; selectedFilter: KnowledgeFilterData | null; isFilterDropdownOpen: boolean; availableFilters: KnowledgeFilterData[]; filterSearchTerm: string; selectedFilterIndex: number; anchorPosition: { x: number; y: number } | null; parsedFilterData: { color?: FilterColor } | null; uploadedFile: File | null; onSubmit: (e: React.FormEvent) => void; onChange: (e: React.ChangeEvent) => void; onKeyDown: (e: React.KeyboardEvent) => void; onFilterSelect: (filter: KnowledgeFilterData | null) => void; onAtClick: () => void; onFilePickerClick: () => void; setSelectedFilter: (filter: KnowledgeFilterData | null) => void; setIsFilterHighlighted: (highlighted: boolean) => void; setIsFilterDropdownOpen: (open: boolean) => void; onFileSelected: (file: File | null) => void; } export const ChatInput = forwardRef( ( { input, loading, isUploading, selectedFilter, isFilterDropdownOpen, availableFilters, filterSearchTerm, selectedFilterIndex, anchorPosition, parsedFilterData, uploadedFile, onSubmit, onChange, onKeyDown, onFilterSelect, onAtClick, onFilePickerClick, setSelectedFilter, setIsFilterHighlighted, setIsFilterDropdownOpen, onFileSelected, }, ref, ) => { const inputRef = useRef(null); const fileInputRef = useRef(null); const [textareaHeight, setTextareaHeight] = useState(0); useImperativeHandle(ref, () => ({ focusInput: () => { inputRef.current?.focus(); }, clickFileInput: () => { fileInputRef.current?.click(); }, })); const handleFilePickerChange = (e: React.ChangeEvent) => { const files = e.target.files; if (files && files.length > 0) { onFileSelected(files[0]); } else { onFileSelected(null); } }; return (
{/* Outer container - flex-col to stack file preview above input */}
{/* File Preview Section - Always above */} {uploadedFile && ( { onFileSelected(null); }} /> )} {/* Main Input Container - flex-row or flex-col based on textarea height */}
40 ? "flex-col" : "flex-row items-center" }`} > {/* Filter + Textarea Section */}
40 ? "w-full" : "flex-1"}`} > {textareaHeight <= 40 && (selectedFilter ? ( { setSelectedFilter(null); setIsFilterHighlighted(false); }} /> ) : ( ))}
setTextareaHeight(height)} maxRows={7} autoComplete="off" minRows={1} placeholder="Ask a question..." disabled={loading} className={`w-full text-sm bg-transparent focus-visible:outline-none resize-none`} rows={1} />
{/* Action Buttons Section */}
40 ? "justify-between w-full" : ""}`} > {textareaHeight > 40 && (selectedFilter ? ( { setSelectedFilter(null); setIsFilterHighlighted(false); }} /> ) : ( ))}
{ setIsFilterDropdownOpen(open); }} > {anchorPosition && (
)} { // Prevent auto focus on the popover content e.preventDefault(); // Keep focus on the input }} >
{filterSearchTerm && (
Searching: @{filterSearchTerm}
)} {availableFilters.length === 0 ? (
No knowledge filters available
) : ( <> {!filterSearchTerm && ( )} {availableFilters .filter((filter) => filter.name .toLowerCase() .includes(filterSearchTerm.toLowerCase()), ) .map((filter, index) => ( ))} {availableFilters.filter((filter) => filter.name .toLowerCase() .includes(filterSearchTerm.toLowerCase()), ).length === 0 && filterSearchTerm && (
No filters match "{filterSearchTerm}"
)} )}
); }, ); ChatInput.displayName = "ChatInput";