import { fetchAvailableModels, generateRandomQuestion } from '@/services/api'; import { selectAvailableModels, selectIsDarkMode, selectIsLoadingModels, selectSelectedModelId, useAgentStore } from '@/stores/agentStore'; import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined'; import LightModeOutlined from '@mui/icons-material/LightModeOutlined'; import SendIcon from '@mui/icons-material/Send'; import ShuffleIcon from '@mui/icons-material/Shuffle'; import SmartToyIcon from '@mui/icons-material/SmartToy'; import { Box, Button, CircularProgress, Container, FormControl, IconButton, InputLabel, MenuItem, Paper, Select, TextField, Typography } from '@mui/material'; import React, { useEffect, useRef, useState } from 'react'; interface WelcomeScreenProps { onStartTask: (instruction: string, modelId: string) => void; isConnected: boolean; } export const WelcomeScreen: React.FC = ({ onStartTask, isConnected }) => { const [customTask, setCustomTask] = useState(''); const [isTyping, setIsTyping] = useState(false); const [isGeneratingQuestion, setIsGeneratingQuestion] = useState(false); const typingIntervalRef = useRef(null); const isDarkMode = useAgentStore(selectIsDarkMode); const toggleDarkMode = useAgentStore((state) => state.toggleDarkMode); const selectedModelId = useAgentStore(selectSelectedModelId); const setSelectedModelId = useAgentStore((state) => state.setSelectedModelId); const availableModels = useAgentStore(selectAvailableModels); const isLoadingModels = useAgentStore(selectIsLoadingModels); const setAvailableModels = useAgentStore((state) => state.setAvailableModels); const setIsLoadingModels = useAgentStore((state) => state.setIsLoadingModels); // Load available models on mount useEffect(() => { const loadModels = async () => { setIsLoadingModels(true); try { const models = await fetchAvailableModels(); setAvailableModels(models); // Set first model as default if current selection is not in the list if (models.length > 0 && !models.includes(selectedModelId)) { setSelectedModelId(models[0]); } } catch (error) { console.error('Failed to load models:', error); // Fallback to empty array on error setAvailableModels([]); } finally { setIsLoadingModels(false); } }; loadModels(); }, []); // eslint-disable-line react-hooks/exhaustive-deps // Clean up typing interval on unmount useEffect(() => { return () => { if (typingIntervalRef.current) { clearInterval(typingIntervalRef.current); } }; }, []); const handleWriteRandomTask = async () => { // Clear any existing typing interval if (typingIntervalRef.current) { clearInterval(typingIntervalRef.current); typingIntervalRef.current = null; } setIsGeneratingQuestion(true); try { const randomTask = await generateRandomQuestion(); // Clear current text setCustomTask(''); setIsTyping(true); // Type effect let currentIndex = 0; typingIntervalRef.current = setInterval(() => { if (currentIndex < randomTask.length) { setCustomTask(randomTask.substring(0, currentIndex + 1)); currentIndex++; } else { if (typingIntervalRef.current) { clearInterval(typingIntervalRef.current); typingIntervalRef.current = null; } setIsTyping(false); } }, 10); // 10ms per character } catch (error) { console.error('Failed to generate question:', error); setIsTyping(false); } finally { setIsGeneratingQuestion(false); } }; const handleCustomTask = () => { if (customTask.trim() && !isTyping) { onStartTask(customTask.trim(), selectedModelId); } }; return ( <> {/* Dark Mode Toggle - Top Right (Absolute to viewport) */} {isDarkMode ? : } {/* Title */} FARA Agent {/* Powered by Microsoft */} Powered by {/* Microsoft Fara link */} Microsoft Fara-7B {/* Separator */} & {/* Modal link */} Modal {/* Subtitle */} AI-Powered Browser Automation {/* Description */} Experience the future of AI automation as FARA operates your browser in real time to complete complex on-screen tasks. Built with{' '} Microsoft Fara-7B , a vision-language model specifically designed for computer use and GUI automation. {/* Task Input Section */} `0 4px 16px ${theme.palette.mode === 'dark' ? 'rgba(79, 134, 198, 0.3)' : 'rgba(79, 134, 198, 0.15)'}`, } : {}, }} > {/* Input Field */} setCustomTask(e.target.value)} onKeyPress={(e) => { if (e.key === 'Enter' && !e.shiftKey && isConnected && customTask.trim() && !isTyping) { handleCustomTask(); } }} disabled={!isConnected || isTyping} multiline rows={3} sx={{ mb: 2, '& .MuiOutlinedInput-root': { borderRadius: 1.5, backgroundColor: 'action.hover', color: 'text.primary', '& fieldset': { borderColor: 'divider', }, '&:hover fieldset': { borderColor: 'text.secondary', }, '&.Mui-focused fieldset': { borderColor: 'primary.main', borderWidth: '2px', }, }, '& .MuiInputBase-input': { color: (theme) => theme.palette.mode === 'dark' ? '#FFFFFF !important' : '#000000 !important', fontWeight: 500, WebkitTextFillColor: (theme) => theme.palette.mode === 'dark' ? '#FFFFFF !important' : '#000000 !important', }, '& .MuiInputBase-input.Mui-disabled': { color: (theme) => theme.palette.mode === 'dark' ? '#FFFFFF !important' : '#000000 !important', WebkitTextFillColor: (theme) => theme.palette.mode === 'dark' ? '#FFFFFF !important' : '#000000 !important', }, '& .MuiInputBase-input::placeholder': { color: 'text.secondary', opacity: 0.7, }, }} /> {/* Model Selection + Buttons Row */} {/* Model Select */} Model {/* Buttons on the right */} {/* Research Notice */} This is a demo of the FARA computer use agent. The agent will browse the web on your behalf. Cold starts may take upto 1 minute for the first prompt after which each step should take 5-10s. Please do not enter any personal or sensitive information. {' '}Task logs will be stored for research purposes. {/* Credits */} Frontend based on{' '} HuggingFace smolagents/computer-use-agent {/* Connection status hint */} {!isConnected && ( Make sure the backend is running on port 8000 )} ); };