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 */} Computer Use Agent {/* Powered by smolagents and E2B */} Powered by {/* smolagents link */} {/* Hugging Face Official Logo */} smolagents {/* GitHub stars badge */} theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.08)' : 'rgba(25, 118, 210, 0.08)', borderRadius: 1, border: '1px solid', borderColor: 'primary.main', }} > 23.7k {/* Separator */} & {/* E2B link */} {/* E2B Logo */} E2B {/* Subtitle */} AI-Powered Computer Use Automation {/* Description */} Experience the future of AI automation as agents operate computers in real time to complete complex on-screen tasks (GUI agents). Built by{' '} Hugging Face , this platform provides intuitive visualization and annotation tools, enabling manual preferential data annotation for advanced agentic AI research. {/* 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 */} Please be aware that by using the demo, you agree that the traces are stored for research purposes. Please do not write any personal information. {/* Connection status hint */} {!isConnected && ( Make sure the backend is running on port 8000 )} ); };