import { createSignal, For, Show, onMount, createEffect } from 'solid-js' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { Card } from '@/components/ui/Card' import { MessageCircle, Brain, Cog, Send, ChevronDown, User, Bot } from 'lucide-solid' import { AIProviderIcon } from '@/components/AIProviderIcon' import { useHaptics } from '@/lib/haptics' interface AIModel { id: string name: string description: string provider: string category: string iconId?: string } interface Message { id: string role: 'user' | 'assistant' content: string timestamp: Date } export const AIChat = () => { const haptics = useHaptics(); const [activeView, setActiveView] = createSignal<'chat' | 'settings'>('chat') const [isSidebarOpen, setIsSidebarOpen] = createSignal(true) // Chat state const [messages, setMessages] = createSignal([ { id: '1', role: 'assistant', content: 'Hello! I\'m your AI assistant. How can I help you today?', timestamp: new Date() } ]) const [inputMessage, setInputMessage] = createSignal('') const [isLoading, setIsLoading] = createSignal(false) // AI Model state const [selectedModel, setSelectedModel] = createSignal('longcat-flash-chat') const [showModelPicker, setShowModelPicker] = createSignal(false) const [aiModels, setAIModels] = createSignal([]) // Initialize AI models onMount(() => { const checkMobile = () => { if (window.innerWidth < 768) { setIsSidebarOpen(false) } } checkMobile() window.addEventListener('resize', checkMobile) // Initialize AI models initializeAIModels() return () => window.removeEventListener('resize', checkMobile) }) const initializeAIModels = () => { const models: AIModel[] = [ { id: 'longcat-flash-chat', name: 'LongCat Flash Chat', description: 'Fast and efficient chat model', provider: 'longcat', category: 'fast', iconId: 'longcat' }, { id: 'longcat-flash-thinking', name: 'LongCat Flash Thinking', description: 'Advanced reasoning model', provider: 'longcat', category: 'thinking', iconId: 'longcat' }, { id: 'mistral-small-latest', name: 'Mistral Small', description: 'Lightweight and fast', provider: 'mistral', category: 'standard', iconId: 'mistral' }, { id: 'mistral-large-latest', name: 'Mistral Large', description: 'Most capable model', provider: 'mistral', category: 'advanced', iconId: 'mistral' }, { id: 'grok-standard', name: 'Grok Standard', description: 'Grok from X', provider: 'grok', category: 'standard', iconId: 'grok' }, { id: 'deepseek-chat', name: 'DeepSeek Chat', description: 'DeepSeek chat model', provider: 'deepseek', category: 'standard', iconId: 'deepseek' }, { id: 'ollama-local', name: 'Ollama Local', description: 'Local Ollama model', provider: 'ollama', category: 'local', iconId: 'ollama' }, { id: 'openrouter-auto', name: 'OpenRouter Auto', description: 'Router over many models', provider: 'openrouter', category: 'standard', iconId: 'openrouter' }, ] setAIModels(models) } const handleSendMessage = async () => { const message = inputMessage().trim() if (!message || isLoading()) return // Add user message const userMessage: Message = { id: Date.now().toString(), content: message, role: 'user', timestamp: new Date() } setMessages(prev => [...prev, userMessage]) setInputMessage('') setIsLoading(true) haptics.impact(); // Impact feedback for sending message try { // Call AI API const response = await callAIAPI(message, selectedModel()) const aiMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: response, timestamp: new Date() } setMessages(prev => [...prev, aiMessage]) haptics.success(); // Success feedback for AI response } catch (error) { console.error('AI API call failed:', error) haptics.error(); // Error feedback // Fallback response const errorMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: 'I apologize, but I encountered an error while processing your request. Please try again later.', timestamp: new Date() } setMessages(prev => [...prev, errorMessage]) } finally { setIsLoading(false) } } const callAIAPI = async (message: string, modelId: string): Promise => { const token = localStorage.getItem('token') const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8080' const response = await fetch(`${apiUrl}/api/v1/ai/chat`, { method: 'POST', headers: { 'Authorization': token ? `Bearer ${token}` : '', 'Content-Type': 'application/json' }, body: JSON.stringify({ message, model: modelId, stream: false }) }) if (!response.ok) { throw new Error(`API call failed: ${response.status}`) } const data = await response.json() return data.response || data.content || 'I understand your message. Let me help you with that.' } // Close model picker when clicking outside createEffect(() => { const handleClickOutside = (e: MouseEvent) => { const target = e.target as HTMLElement if (!target.closest('#model-picker-container')) { setShowModelPicker(false) } } if (showModelPicker()) { document.addEventListener('click', handleClickOutside) return () => document.removeEventListener('click', handleClickOutside) } }) const startNewChat = () => { setMessages([{ id: '1', role: 'assistant', content: 'Hello! I\'m your AI assistant. How can I help you today?', timestamp: new Date() }]) setInputMessage('') } return (
{/* Header */}
{/* AI Logo */}

AI Assistant

Your intelligent workspace companion

{/* View Switcher */}
{/* Sidebar */} {/* Main Content */}
{/* Chat View */}
{/* Messages Area */}
{message => (
{message.role === 'user' ? ( ) : ( )}

{message.content}

{message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}

{message.role === 'user' && (
)}
)}
{isLoading() && (
AI is thinking...
)}
{/* Input Area */}
{/* AI Model Switcher */}
{/* Model Picker Dropdown */}

Select AI Model

Choose the best model for your needs

{model => ( )}
setInputMessage((e.currentTarget as HTMLInputElement).value)} placeholder="Type your message..." class="flex-1 pr-12 rounded-xl border-border/50 bg-background/95 backdrop-blur-sm shadow-sm transition-all duration-200 focus:shadow-md focus:border-primary/50" onKeyDown={(e: KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey && inputMessage().trim()) { handleSendMessage() } }} />
{/* Settings View */}

AI Settings

Configure your AI models and preferences

Available AI Models

{(model) => (
{model.name}

{model.description}

{model.provider} {model.category}
)}

Current Selection

m.id === selectedModel())?.iconId || 'longcat'} size="1.5rem" class="text-primary" />

{aiModels().find(m => m.id === selectedModel())?.name}

{aiModels().find(m => m.id === selectedModel())?.description}

) } export default AIChat