import { IconSend, IconWand } from '@tabler/icons-react' import { useState, useRef, useEffect } from 'react' import classNames from '~/lib/classNames' import { ChatMessage } from '../../../types/chat' import ChatMessageBubble from './ChatMessageBubble' import ChatAssistantAvatar from './ChatAssistantAvatar' import BouncingDots from '../BouncingDots' import StyledModal from '../StyledModal' import api from '~/lib/api' import { DEFAULT_QUERY_REWRITE_MODEL } from '../../../constants/ollama' import { useNotifications } from '~/context/NotificationContext' import { usePage } from '@inertiajs/react' interface ChatInterfaceProps { messages: ChatMessage[] onSendMessage: (message: string) => void isLoading?: boolean chatSuggestions?: string[] chatSuggestionsEnabled?: boolean chatSuggestionsLoading?: boolean rewriteModelAvailable?: boolean } export default function ChatInterface({ messages, onSendMessage, isLoading = false, chatSuggestions = [], chatSuggestionsEnabled = false, chatSuggestionsLoading = false, rewriteModelAvailable = false }: ChatInterfaceProps) { const { aiAssistantName } = usePage<{ aiAssistantName: string }>().props const { addNotification } = useNotifications() const [input, setInput] = useState('') const [downloadDialogOpen, setDownloadDialogOpen] = useState(false) const [isDownloading, setIsDownloading] = useState(false) const messagesEndRef = useRef(null) const textareaRef = useRef(null) const handleDownloadModel = async () => { setIsDownloading(true) try { await api.downloadModel(DEFAULT_QUERY_REWRITE_MODEL) addNotification({ type: 'success', message: 'Model download queued' }) } catch (error) { addNotification({ type: 'error', message: 'Failed to queue model download' }) } finally { setIsDownloading(false) setDownloadDialogOpen(false) } } const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) } useEffect(() => { scrollToBottom() }, [messages]) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (input.trim() && !isLoading) { onSendMessage(input.trim()) setInput('') if (textareaRef.current) { textareaRef.current.style.height = 'auto' } } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSubmit(e) } } const handleInput = (e: React.ChangeEvent) => { setInput(e.target.value) // Auto-resize textarea e.target.style.height = 'auto' e.target.style.height = `${Math.min(e.target.scrollHeight, 200)}px` } return (
{messages.length === 0 ? (

Start a conversation

Interact with your installed language models directly in the Command Center.

{chatSuggestionsEnabled && chatSuggestions && chatSuggestions.length > 0 && !chatSuggestionsLoading && (

Suggestions:

{chatSuggestions.map((suggestion, index) => ( ))}
)} {/* Display bouncing dots while loading suggestions */} {chatSuggestionsEnabled && chatSuggestionsLoading && } {!chatSuggestionsEnabled && (
Need some inspiration? Enable chat suggestions in settings to get started with example prompts.
)}
) : ( <> {messages.map((message) => (
{message.role === 'assistant' && }
))} {/* Loading/thinking indicator */} {isLoading && (
)}
)}