diff --git a/frontend/src/app/chat/components/chat-input.tsx b/frontend/src/app/chat/components/chat-input.tsx index e63a5236..85563886 100644 --- a/frontend/src/app/chat/components/chat-input.tsx +++ b/frontend/src/app/chat/components/chat-input.tsx @@ -1,282 +1,284 @@ import { Check, Funnel, Loader2, Plus, X } from "lucide-react"; -import TextareaAutosize from "react-textarea-autosize"; import { forwardRef, useImperativeHandle, useRef } from "react"; +import TextareaAutosize from "react-textarea-autosize"; +import type { FilterColor } from "@/components/filter-icon-popover"; import { filterAccentClasses } from "@/components/knowledge-filter-panel"; import { Button } from "@/components/ui/button"; import { - Popover, - PopoverAnchor, - PopoverContent, + Popover, + PopoverAnchor, + PopoverContent, } from "@/components/ui/popover"; import type { KnowledgeFilterData } from "../types"; -import { FilterColor } from "@/components/filter-icon-popover"; export interface ChatInputHandle { - focusInput: () => void; - clickFileInput: () => void; + 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; - textareaHeight: number; - parsedFilterData: { color?: FilterColor } | null; - onSubmit: (e: React.FormEvent) => void; - onChange: (e: React.ChangeEvent) => void; - onKeyDown: (e: React.KeyboardEvent) => void; - onHeightChange: (height: number) => void; - onFilterSelect: (filter: KnowledgeFilterData | null) => void; - onAtClick: () => void; - onFilePickerChange: (e: React.ChangeEvent) => void; - onFilePickerClick: () => void; - setSelectedFilter: (filter: KnowledgeFilterData | null) => void; - setIsFilterHighlighted: (highlighted: boolean) => void; - setIsFilterDropdownOpen: (open: boolean) => void; + input: string; + loading: boolean; + isUploading: boolean; + selectedFilter: KnowledgeFilterData | null; + isFilterDropdownOpen: boolean; + availableFilters: KnowledgeFilterData[]; + filterSearchTerm: string; + selectedFilterIndex: number; + anchorPosition: { x: number; y: number } | null; + textareaHeight: number; + parsedFilterData: { color?: FilterColor } | null; + onSubmit: (e: React.FormEvent) => void; + onChange: (e: React.ChangeEvent) => void; + onKeyDown: (e: React.KeyboardEvent) => void; + onHeightChange: (height: number) => void; + onFilterSelect: (filter: KnowledgeFilterData | null) => void; + onAtClick: () => void; + onFilePickerChange: (e: React.ChangeEvent) => void; + onFilePickerClick: () => void; + setSelectedFilter: (filter: KnowledgeFilterData | null) => void; + setIsFilterHighlighted: (highlighted: boolean) => void; + setIsFilterDropdownOpen: (open: boolean) => void; } -export const ChatInput = forwardRef(( - { - input, - loading, - isUploading, - selectedFilter, - isFilterDropdownOpen, - availableFilters, - filterSearchTerm, - selectedFilterIndex, - anchorPosition, - textareaHeight, - parsedFilterData, - onSubmit, - onChange, - onKeyDown, - onHeightChange, - onFilterSelect, - onAtClick, - onFilePickerChange, - onFilePickerClick, - setSelectedFilter, - setIsFilterHighlighted, - setIsFilterDropdownOpen, - }, - ref -) => { - const inputRef = useRef(null); - const fileInputRef = useRef(null); +export const ChatInput = forwardRef( + ( + { + input, + loading, + isUploading, + selectedFilter, + isFilterDropdownOpen, + availableFilters, + filterSearchTerm, + selectedFilterIndex, + anchorPosition, + textareaHeight, + parsedFilterData, + onSubmit, + onChange, + onKeyDown, + onHeightChange, + onFilterSelect, + onAtClick, + onFilePickerChange, + onFilePickerClick, + setSelectedFilter, + setIsFilterHighlighted, + setIsFilterDropdownOpen, + }, + ref, + ) => { + const inputRef = useRef(null); + const fileInputRef = useRef(null); - useImperativeHandle(ref, () => ({ - focusInput: () => { - inputRef.current?.focus(); - }, - clickFileInput: () => { - fileInputRef.current?.click(); - }, - })); + useImperativeHandle(ref, () => ({ + focusInput: () => { + inputRef.current?.focus(); + }, + clickFileInput: () => { + fileInputRef.current?.click(); + }, + })); - return ( -
-
-
-
- {selectedFilter && ( -
- - @filter:{selectedFilter.name} - - -
- )} -
- - {/* Safe area at bottom for buttons */} -
-
-
- - - { - 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}" -
- )} - - )} -
-
- - - - -
-
- ); -}); + return ( +
+
+
+
+ {selectedFilter && ( +
+ + @filter:{selectedFilter.name} + + +
+ )} +
+ + {/* Safe area at bottom for buttons */} +
+
+
+ + + { + 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";