let currentEventSource = null; function createTask() { const promptInput = document.getElementById('prompt-input'); const prompt = promptInput.value.trim(); if (!prompt) { alert("Please enter a valid prompt"); promptInput.focus(); return; } if (currentEventSource) { currentEventSource.close(); currentEventSource = null; } const container = document.getElementById('task-container'); container.innerHTML = '
Initializing task...
'; document.getElementById('input-container').classList.add('bottom'); fetch('/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }) }) .then(response => { if (!response.ok) { return response.json().then(err => { throw new Error(err.detail || 'Request failed') }); } return response.json(); }) .then(data => { if (!data.task_id) { throw new Error('Invalid task ID'); } setupSSE(data.task_id); loadHistory(); promptInput.value = ''; }) .catch(error => { container.innerHTML = `
Error: ${error.message}
`; console.error('Failed to create task:', error); }); } function setupSSE(taskId) { let retryCount = 0; const maxRetries = 3; const retryDelay = 2000; let lastResultContent = ''; const container = document.getElementById('task-container'); function connect() { const eventSource = new EventSource(`/tasks/${taskId}/events`); currentEventSource = eventSource; let heartbeatTimer = setInterval(() => { container.innerHTML += '
·
'; }, 5000); // Initial polling fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Initial status fetch failed:', error); }); const handleEvent = (event, type) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); container.classList.add('active'); const stepContainer = ensureStepContainer(container); const { formattedContent, timestamp } = formatStepContent(data, type); const step = createStepElement(type, formattedContent, timestamp); stepContainer.appendChild(step); autoScroll(stepContainer); fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error(`Error handling ${type} event:`, e); } }; const eventTypes = ['think', 'tool', 'act', 'log', 'run', 'message']; eventTypes.forEach(type => { eventSource.addEventListener(type, (event) => handleEvent(event, type)); }); eventSource.addEventListener('complete', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); lastResultContent = data.result || ''; container.innerHTML += `
✅ Task completed
${lastResultContent}
`; fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Final status update failed:', error); }); eventSource.close(); currentEventSource = null; } catch (e) { console.error('Error handling complete event:', e); } }); eventSource.addEventListener('error', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.innerHTML += `
❌ Error: ${data.message}
`; eventSource.close(); currentEventSource = null; } catch (e) { console.error('Error handling failed:', e); } }); eventSource.onerror = (err) => { if (eventSource.readyState === EventSource.CLOSED) return; console.error('SSE connection error:', err); clearInterval(heartbeatTimer); eventSource.close(); fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { if (task.status === 'completed' || task.status === 'failed') { updateTaskStatus(task); if (task.status === 'completed') { container.innerHTML += `
✅ Task completed
`; } else { container.innerHTML += `
❌ Error: ${task.error || 'Task failed'}
`; } } else if (retryCount < maxRetries) { retryCount++; container.innerHTML += `
⚠ Connection lost, retrying in ${retryDelay/1000} seconds (${retryCount}/${maxRetries})...
`; setTimeout(connect, retryDelay); } else { container.innerHTML += `
⚠ Connection lost, please try refreshing the page
`; } }) .catch(error => { console.error('Task status check failed:', error); if (retryCount < maxRetries) { retryCount++; setTimeout(connect, retryDelay); } }); }; } connect(); } function loadHistory() { fetch('/tasks') .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`request failure: ${response.status} - ${text.substring(0, 100)}`); }); } return response.json(); }) .then(tasks => { const listContainer = document.getElementById('task-list'); listContainer.innerHTML = tasks.map(task => `
${task.prompt}
${new Date(task.created_at).toLocaleString()} - ${task.status || 'Unknown state'}
`).join(''); }) .catch(error => { console.error('Failed to load history records:', error); const listContainer = document.getElementById('task-list'); listContainer.innerHTML = `
Load Fail: ${error.message}
`; }); } function ensureStepContainer(container) { let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } return stepContainer; } function formatStepContent(data, eventType) { return { formattedContent: data.result, timestamp: new Date().toLocaleTimeString() }; } function createStepElement(type, content, timestamp) { const step = document.createElement('div'); // Executing step const stepRegex = /Executing step (\d+)\/(\d+)/; if (type === 'log' && stepRegex.test(content)) { const match = content.match(stepRegex); const currentStep = parseInt(match[1]); const totalSteps = parseInt(match[2]); step.className = 'step-divider'; step.innerHTML = `
${currentStep}
${currentStep}/${totalSteps}
`; } else if (type === 'act') { // Check if it contains information about file saving const saveRegex = /Content successfully saved to (.+)/; const match = content.match(saveRegex); step.className = `step-item ${type}`; if (match && match[1]) { const filePath = match[1].trim(); const fileName = filePath.split('/').pop(); const fileExtension = fileName.split('.').pop().toLowerCase(); // Handling different types of files let fileInteractionHtml = ''; if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(fileExtension)) { fileInteractionHtml = `
${fileName} ⬇️ 下载图片
`; } else if (['mp3', 'wav', 'ogg'].includes(fileExtension)) { fileInteractionHtml = `
⬇️ 下载音频
`; } else if (['html', 'js', 'py'].includes(fileExtension)) { fileInteractionHtml = `
⬇️ 下载文件
`; } else { fileInteractionHtml = `
⬇️ 下载文件: ${fileName}
`; } step.innerHTML = `
${getEventIcon(type)} [${timestamp}] ${getEventLabel(type)}:
${content}
${fileInteractionHtml}
`; } else { step.innerHTML = `
${getEventIcon(type)} [${timestamp}] ${getEventLabel(type)}:
${content}
`; } } else { step.className = `step-item ${type}`; step.innerHTML = `
${getEventIcon(type)} [${timestamp}] ${getEventLabel(type)}:
${content}
`; } return step; } function autoScroll(element) { requestAnimationFrame(() => { element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' }); }); setTimeout(() => { element.scrollTop = element.scrollHeight; }, 100); } function getEventIcon(eventType) { const icons = { 'think': '🤔', 'tool': '🛠️', 'act': '🚀', 'result': '🏁', 'error': '❌', 'complete': '✅', 'log': '📝', 'run': '⚙️' }; return icons[eventType] || 'ℹ️'; } function getEventLabel(eventType) { const labels = { 'think': 'Thinking', 'tool': 'Using Tool', 'act': 'Action', 'result': 'Result', 'error': 'Error', 'complete': 'Complete', 'log': 'Log', 'run': 'Running' }; return labels[eventType] || 'Info'; } function updateTaskStatus(task) { const statusBar = document.getElementById('status-bar'); if (!statusBar) return; if (task.status === 'completed') { statusBar.innerHTML = `✅ Task completed`; if (currentEventSource) { currentEventSource.close(); currentEventSource = null; } } else if (task.status === 'failed') { statusBar.innerHTML = `❌ Task failed: ${task.error || 'Unknown error'}`; if (currentEventSource) { currentEventSource.close(); currentEventSource = null; } } else { statusBar.innerHTML = `⚙️ Task running: ${task.status}`; } } // Display full screen image function showFullImage(imageSrc) { const modal = document.getElementById('image-modal'); if (!modal) { const modalDiv = document.createElement('div'); modalDiv.id = 'image-modal'; modalDiv.className = 'image-modal'; modalDiv.innerHTML = ` × `; document.body.appendChild(modalDiv); const closeBtn = modalDiv.querySelector('.close-modal'); closeBtn.addEventListener('click', () => { modalDiv.classList.remove('active'); }); modalDiv.addEventListener('click', (e) => { if (e.target === modalDiv) { modalDiv.classList.remove('active'); } }); setTimeout(() => modalDiv.classList.add('active'), 10); } else { document.getElementById('full-image').src = imageSrc; modal.classList.add('active'); } } // Simulate running Python files function simulateRunPython(filePath) { let modal = document.getElementById('python-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'python-modal'; modal.className = 'python-modal'; modal.innerHTML = `
×
Loading Python file contents...
`; document.body.appendChild(modal); const closeBtn = modal.querySelector('.close-modal'); closeBtn.addEventListener('click', () => { modal.classList.remove('active'); }); } modal.classList.add('active'); // Load Python file content fetch(filePath) .then(response => response.text()) .then(code => { const outputDiv = modal.querySelector('.python-output'); outputDiv.innerHTML = ''; const codeElement = document.createElement('pre'); codeElement.textContent = code; codeElement.style.marginBottom = '20px'; codeElement.style.padding = '10px'; codeElement.style.borderBottom = '1px solid #444'; outputDiv.appendChild(codeElement); // Add simulation run results const resultElement = document.createElement('div'); resultElement.innerHTML = `
> Simulated operation output:
#This is the result of Python code simulation run
#The actual operational results may vary

# Running ${filePath.split('/').pop()}...
print("Hello from Python Simulated environment!")

# Code execution completed
`; outputDiv.appendChild(resultElement); }) .catch(error => { console.error('Error loading Python file:', error); const outputDiv = modal.querySelector('.python-output'); outputDiv.innerHTML = `Error loading file: ${error.message}`; }); } document.addEventListener('DOMContentLoaded', () => { loadHistory(); document.getElementById('prompt-input').addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); createTask(); } }); const historyToggle = document.getElementById('history-toggle'); if (historyToggle) { historyToggle.addEventListener('click', () => { const historyPanel = document.getElementById('history-panel'); if (historyPanel) { historyPanel.classList.toggle('open'); historyToggle.classList.toggle('active'); } }); } const clearButton = document.getElementById('clear-btn'); if (clearButton) { clearButton.addEventListener('click', () => { document.getElementById('prompt-input').value = ''; document.getElementById('prompt-input').focus(); }); } // Add keyboard event listener to close modal boxes document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const imageModal = document.getElementById('image-modal'); if (imageModal && imageModal.classList.contains('active')) { imageModal.classList.remove('active'); } const pythonModal = document.getElementById('python-modal'); if (pythonModal && pythonModal.classList.contains('active')) { pythonModal.classList.remove('active'); } } }); });