diff --git a/static/main.js b/static/main.js
index fbf61ff..5015c34 100644
--- a/static/main.js
+++ b/static/main.js
@@ -5,7 +5,7 @@ function createTask() {
const prompt = promptInput.value.trim();
if (!prompt) {
- alert("请输入有效的提示内容");
+ alert("Please enter a valid prompt");
promptInput.focus();
return;
}
@@ -16,7 +16,7 @@ function createTask() {
}
const container = document.getElementById('task-container');
- container.innerHTML = '
任务初始化中...
';
+ container.innerHTML = 'Initializing task...
';
document.getElementById('input-container').classList.add('bottom');
fetch('/tasks', {
@@ -28,20 +28,20 @@ function createTask() {
})
.then(response => {
if (!response.ok) {
- return response.json().then(err => { throw new Error(err.detail || '请求失败') });
+ return response.json().then(err => { throw new Error(err.detail || 'Request failed') });
}
return response.json();
})
.then(data => {
if (!data.task_id) {
- throw new Error('无效的任务ID');
+ throw new Error('Invalid task ID');
}
setupSSE(data.task_id);
loadHistory();
})
.catch(error => {
- container.innerHTML = `错误: ${error.message}
`;
- console.error('创建任务失败:', error);
+ container.innerHTML = `Error: ${error.message}
`;
+ console.error('Failed to create task:', error);
});
}
@@ -49,13 +49,13 @@ function setupSSE(taskId) {
let retryCount = 0;
const maxRetries = 3;
const retryDelay = 2000;
-
+
function connect() {
const eventSource = new EventSource(`/tasks/${taskId}/events`);
currentEventSource = eventSource;
const container = document.getElementById('task-container');
-
+
let heartbeatTimer = setInterval(() => {
container.innerHTML += '·
';
}, 5000);
@@ -67,13 +67,13 @@ function setupSSE(taskId) {
updateTaskStatus(task);
})
.catch(error => {
- console.error('轮询失败:', error);
+ console.error('Polling failed:', error);
});
}, 10000);
if (!eventSource._listenersAdded) {
eventSource._listenersAdded = true;
-
+
let lastResultContent = '';
eventSource.addEventListener('status', (event) => {
clearInterval(heartbeatTimer);
@@ -92,9 +92,9 @@ function setupSSE(taskId) {
stepContainer = container.querySelector('.step-container');
}
- // 保存result内容
+ // Save result content
if (data.steps && data.steps.length > 0) {
- // 遍历所有步骤,找到最后一个result类型
+ // Iterate through all steps, find the last result type
for (let i = data.steps.length - 1; i >= 0; i--) {
if (data.steps[i].type === 'result') {
lastResultContent = data.steps[i].result;
@@ -116,24 +116,24 @@ function setupSSE(taskId) {
`;
}).join('');
-
+
// Auto-scroll to bottom
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
} catch (e) {
- console.error('状态更新失败:', e);
+ console.error('Status update failed:', e);
}
});
- // 添加对think事件的处理
+ // Add handler for think event
eventSource.addEventListener('think', (event) => {
clearInterval(heartbeatTimer);
try {
const data = JSON.parse(event.data);
container.querySelector('.loading')?.remove();
-
+
let stepContainer = container.querySelector('.step-container');
if (!stepContainer) {
container.innerHTML = '';
@@ -142,7 +142,7 @@ function setupSSE(taskId) {
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
const step = document.createElement('div');
step.className = 'step-item think';
step.innerHTML = `
@@ -151,34 +151,34 @@ function setupSSE(taskId) {
${content}
`;
-
+
stepContainer.appendChild(step);
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
- // 更新任务状态
+ // Update task status
fetch(`/tasks/${taskId}`)
.then(response => response.json())
.then(task => {
updateTaskStatus(task);
})
.catch(error => {
- console.error('状态更新失败:', error);
+ console.error('Status update failed:', error);
});
} catch (e) {
- console.error('思考事件处理失败:', e);
+ console.error('Think event handling failed:', e);
}
});
- // 添加对tool事件的处理
+ // Add handler for tool event
eventSource.addEventListener('tool', (event) => {
clearInterval(heartbeatTimer);
try {
const data = JSON.parse(event.data);
container.querySelector('.loading')?.remove();
-
+
let stepContainer = container.querySelector('.step-container');
if (!stepContainer) {
container.innerHTML = '';
@@ -187,7 +187,7 @@ function setupSSE(taskId) {
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
const step = document.createElement('div');
step.className = 'step-item tool';
step.innerHTML = `
@@ -196,34 +196,34 @@ function setupSSE(taskId) {
${content}
`;
-
+
stepContainer.appendChild(step);
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
- // 更新任务状态
+ // Update task status
fetch(`/tasks/${taskId}`)
.then(response => response.json())
.then(task => {
updateTaskStatus(task);
})
.catch(error => {
- console.error('状态更新失败:', error);
+ console.error('Status update failed:', error);
});
} catch (e) {
- console.error('工具事件处理失败:', e);
+ console.error('Tool event handling failed:', e);
}
});
- // 添加对act事件的处理
+ // Add handler for act event
eventSource.addEventListener('act', (event) => {
clearInterval(heartbeatTimer);
try {
const data = JSON.parse(event.data);
container.querySelector('.loading')?.remove();
-
+
let stepContainer = container.querySelector('.step-container');
if (!stepContainer) {
container.innerHTML = '';
@@ -232,7 +232,7 @@ function setupSSE(taskId) {
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
const step = document.createElement('div');
step.className = 'step-item act';
step.innerHTML = `
@@ -241,35 +241,34 @@ function setupSSE(taskId) {
${content}
`;
-
+
stepContainer.appendChild(step);
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
- // 更新任务状态
+ // Update task status
fetch(`/tasks/${taskId}`)
.then(response => response.json())
.then(task => {
updateTaskStatus(task);
})
.catch(error => {
- console.error('状态更新失败:', error);
+ console.error('Status update failed:', error);
});
} catch (e) {
- console.error('执行事件处理失败:', e);
+ console.error('Act event handling failed:', e);
}
});
- // 添加对run事件的处理
- // 添加对log事件的处理
+ // Add handler for log event
eventSource.addEventListener('log', (event) => {
clearInterval(heartbeatTimer);
try {
const data = JSON.parse(event.data);
container.querySelector('.loading')?.remove();
-
+
let stepContainer = container.querySelector('.step-container');
if (!stepContainer) {
container.innerHTML = '';
@@ -278,7 +277,7 @@ function setupSSE(taskId) {
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
const step = document.createElement('div');
step.className = 'step-item log';
step.innerHTML = `
@@ -287,24 +286,24 @@ function setupSSE(taskId) {
${content}
`;
-
+
stepContainer.appendChild(step);
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
- // 更新任务状态
+ // Update task status
fetch(`/tasks/${taskId}`)
.then(response => response.json())
.then(task => {
updateTaskStatus(task);
})
.catch(error => {
- console.error('状态更新失败:', error);
+ console.error('Status update failed:', error);
});
} catch (e) {
- console.error('日志事件处理失败:', e);
+ console.error('Log event handling failed:', e);
}
});
@@ -313,7 +312,7 @@ function setupSSE(taskId) {
try {
const data = JSON.parse(event.data);
container.querySelector('.loading')?.remove();
-
+
let stepContainer = container.querySelector('.step-container');
if (!stepContainer) {
container.innerHTML = '';
@@ -322,7 +321,7 @@ function setupSSE(taskId) {
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
const step = document.createElement('div');
step.className = 'step-item run';
step.innerHTML = `
@@ -331,24 +330,24 @@ function setupSSE(taskId) {
${content}
`;
-
+
stepContainer.appendChild(step);
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
- // 更新任务状态
+ // Update task status
fetch(`/tasks/${taskId}`)
.then(response => response.json())
.then(task => {
updateTaskStatus(task);
})
.catch(error => {
- console.error('状态更新失败:', error);
+ console.error('Status update failed:', error);
});
} catch (e) {
- console.error('运行事件处理失败:', e);
+ console.error('Run event handling failed:', e);
}
});
@@ -363,35 +362,35 @@ function setupSSE(taskId) {
container.innerHTML = '';
stepContainer = container.querySelector('.step-container');
}
-
+
// Create new step element
const step = document.createElement('div');
step.className = `step-item ${data.type || 'step'}`;
-
+
// Format content and timestamp
const content = data.result;
const timestamp = new Date().toLocaleTimeString();
-
+
step.innerHTML = `
${getEventIcon(data.type)} [${timestamp}] ${getEventLabel(data.type)}:
${content}
`;
-
+
// Add step to container with animation
stepContainer.prepend(step);
setTimeout(() => {
step.classList.add('show');
}, 10);
-
+
// Auto-scroll to bottom
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
});
} catch (e) {
- console.error('消息处理失败:', e);
+ console.error('Message handling failed:', e);
}
});
@@ -403,13 +402,13 @@ function setupSSE(taskId) {
clearInterval(pollInterval);
container.innerHTML += `
-
✅ 任务完成
+
✅ Task completed
${lastResultContent}
`;
eventSource.close();
currentEventSource = null;
- lastResultContent = ''; // 清空结果内容
+ lastResultContent = ''; // Clear result content
});
eventSource.addEventListener('error', (event) => {
@@ -419,17 +418,17 @@ function setupSSE(taskId) {
const data = JSON.parse(event.data);
container.innerHTML += `
- ❌ 错误: ${data.message}
+ ❌ Error: ${data.message}
`;
eventSource.close();
currentEventSource = null;
} catch (e) {
- console.error('错误处理失败:', e);
+ console.error('Error handling failed:', e);
}
});
}
-
+
container.scrollTo({
top: container.scrollHeight,
behavior: 'smooth'
@@ -439,30 +438,30 @@ function setupSSE(taskId) {
if (isTaskComplete) {
return;
}
-
- console.error('SSE连接错误:', err);
+
+ console.error('SSE connection error:', err);
clearInterval(heartbeatTimer);
clearInterval(pollInterval);
eventSource.close();
-
+
if (retryCount < maxRetries) {
retryCount++;
container.innerHTML += `
- ⚠ 连接中断,${retryDelay/1000}秒后重试 (${retryCount}/${maxRetries})...
+ ⚠ Connection lost, retrying in ${retryDelay/1000} seconds (${retryCount}/${maxRetries})...
`;
setTimeout(connect, retryDelay);
} else {
container.innerHTML += `
- ⚠ 连接中断,请尝试刷新页面
+ ⚠ Connection lost, please try refreshing the page
`;
}
};
}
-
+
connect();
}
@@ -474,112 +473,223 @@ function getEventIcon(eventType) {
case 'result': return '🏁';
case 'error': return '❌';
case 'complete': return '✅';
- case 'warning': return '⚠️';
case 'log': return '📝';
- default: return '⚡';
+ case 'run': return '⚙️';
+ default: return 'ℹ️';
}
}
function getEventLabel(eventType) {
switch(eventType) {
- case 'think': return '思考';
- case 'tool': return '工具执行';
- case 'act': return '执行';
- case 'result': return '结果';
- case 'error': return '错误';
- case 'complete': return '完成';
- case 'warning': return '警告';
- case 'log': return '日志';
- default: return '步骤';
+ case 'think': return 'Thinking';
+ case 'tool': return 'Using Tool';
+ case 'act': return 'Action';
+ case 'result': return 'Result';
+ case 'error': return 'Error';
+ case 'complete': return 'Complete';
+ case 'log': return 'Log';
+ case 'run': return 'Running';
+ default: return 'Info';
}
}
-function formatContent(content) {
- // Remove timestamp and log level prefixes
- content = content.replace(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} \| [A-Z]+\s*\| /gm, '');
- // Format the remaining content
- return content
- .replace(/\n/g, '
')
- .replace(/ /g, ' ')
- .replace(/✨ Manus's thoughts:/g, '')
- .replace(/🛠️ Manus selected/g, '')
- .replace(/🧰 Tools being prepared:/g, '')
- .replace(/🔧 Activating tool:/g, '')
- .replace(/🎯 Tool/g, '')
- .replace(/📝 Oops!/g, '')
- .replace(/🏁 Special tool/g, '');
-}
-
function updateTaskStatus(task) {
- const taskCard = document.querySelector(`.task-card[data-task-id="${task.id}"]`);
- if (taskCard) {
- const statusEl = taskCard.querySelector('.task-meta .status');
- if (statusEl) {
- statusEl.className = `status-${task.status ? task.status.toLowerCase() : 'unknown'}`;
- statusEl.textContent = task.status || '未知状态';
- }
+ const statusBar = document.getElementById('status-bar');
+ if (!statusBar) return;
+
+ if (task.status === 'completed') {
+ statusBar.innerHTML = `✅ Task completed`;
+ } else if (task.status === 'failed') {
+ statusBar.innerHTML = `❌ Task failed: ${task.error || 'Unknown error'}`;
+ } else {
+ statusBar.innerHTML = `⚙️ Task running: ${task.status}`;
}
}
function loadHistory() {
fetch('/tasks')
- .then(response => {
- if (!response.ok) {
- return response.text().then(text => {
- throw new Error(`请求失败: ${response.status} - ${text.substring(0, 100)}`);
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Failed to load history');
+ }
+ return response.json();
+ })
+ .then(tasks => {
+ const historyContainer = document.getElementById('history-container');
+ if (!historyContainer) return;
+
+ historyContainer.innerHTML = '';
+
+ if (tasks.length === 0) {
+ historyContainer.innerHTML = 'No recent tasks
';
+ return;
+ }
+
+ const historyList = document.createElement('div');
+ historyList.className = 'history-list';
+
+ tasks.forEach(task => {
+ const taskItem = document.createElement('div');
+ taskItem.className = `history-item ${task.status}`;
+ taskItem.innerHTML = `
+ ${task.prompt}
+
+ ${new Date(task.created_at).toLocaleString()}
+ ${getStatusIcon(task.status)}
+
+ `;
+ taskItem.addEventListener('click', () => {
+ loadTask(task.id);
+ });
+ historyList.appendChild(taskItem);
});
- }
- 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 || '未知状态'}
-
-
-
- `).join('');
- })
- .catch(error => {
- console.error('加载历史记录失败:', error);
- const listContainer = document.getElementById('task-list');
- listContainer.innerHTML = `加载失败: ${error.message}
`;
- });
+
+ historyContainer.appendChild(historyList);
+ })
+ .catch(error => {
+ console.error('Failed to load history:', error);
+ const historyContainer = document.getElementById('history-container');
+ if (historyContainer) {
+ historyContainer.innerHTML = `Failed to load history: ${error.message}
`;
+ }
+ });
}
-document.addEventListener('DOMContentLoaded', function() {
+function getStatusIcon(status) {
+ switch(status) {
+ case 'completed': return '✅';
+ case 'failed': return '❌';
+ case 'running': return '⚙️';
+ default: return '⏳';
+ }
+}
+
+function loadTask(taskId) {
+ if (currentEventSource) {
+ currentEventSource.close();
+ currentEventSource = null;
+ }
+
+ const container = document.getElementById('task-container');
+ container.innerHTML = 'Loading task...
';
+ document.getElementById('input-container').classList.add('bottom');
+
+ fetch(`/tasks/${taskId}`)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Failed to load task');
+ }
+ return response.json();
+ })
+ .then(task => {
+ if (task.status === 'running') {
+ setupSSE(taskId);
+ } else {
+ displayTask(task);
+ }
+ })
+ .catch(error => {
+ console.error('Failed to load task:', error);
+ container.innerHTML = `Failed to load task: ${error.message}
`;
+ });
+}
+
+function displayTask(task) {
+ const container = document.getElementById('task-container');
+ container.innerHTML = '';
+ container.classList.add('active');
+
const welcomeMessage = document.querySelector('.welcome-message');
if (welcomeMessage) {
- welcomeMessage.style.display = 'flex';
+ welcomeMessage.style.display = 'none';
}
-
- // 监听任务容器显示状态
- const taskContainer = document.getElementById('task-container');
- if (taskContainer) {
- const observer = new MutationObserver((mutations) => {
- mutations.forEach(mutation => {
- if (mutation.attributeName === 'class') {
- const welcomeMessage = document.querySelector('.welcome-message');
- if (taskContainer.classList.contains('active')) {
- if (welcomeMessage) {
- welcomeMessage.style.display = 'none';
- }
- } else {
- if (welcomeMessage) {
- welcomeMessage.style.display = 'block';
- }
- }
- }
- });
+
+ const stepContainer = document.createElement('div');
+ stepContainer.className = 'step-container';
+
+ if (task.steps && task.steps.length > 0) {
+ task.steps.forEach(step => {
+ const stepItem = document.createElement('div');
+ stepItem.className = `step-item ${step.type || 'step'}`;
+
+ const content = step.result;
+ const timestamp = new Date(step.timestamp || task.created_at).toLocaleTimeString();
+
+ stepItem.innerHTML = `
+
+
${getEventIcon(step.type)} [${timestamp}] ${getEventLabel(step.type)}:
+
${content}
+
+ `;
+
+ stepContainer.appendChild(stepItem);
});
-
- observer.observe(taskContainer, {
- attributes: true
+ } else {
+ stepContainer.innerHTML = 'No steps recorded for this task
';
+ }
+
+ container.appendChild(stepContainer);
+
+ if (task.status === 'completed') {
+ let lastResultContent = '';
+ if (task.steps && task.steps.length > 0) {
+ for (let i = task.steps.length - 1; i >= 0; i--) {
+ if (task.steps[i].type === 'result') {
+ lastResultContent = task.steps[i].result;
+ break;
+ }
+ }
+ }
+
+ container.innerHTML += `
+
+
✅ Task completed
+
${lastResultContent}
+
+ `;
+ } else if (task.status === 'failed') {
+ container.innerHTML += `
+
+ ❌ Error: ${task.error || 'Unknown error'}
+
+ `;
+ }
+
+ updateTaskStatus(task);
+}
+
+// Initialize the app when the DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+ loadHistory();
+
+ // Set up event listeners
+ document.getElementById('create-task-btn').addEventListener('click', createTask);
+ document.getElementById('prompt-input').addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ createTask();
+ }
+ });
+
+ // Show history button functionality
+ 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');
+ }
+ });
+ }
+
+ // Clear button functionality
+ const clearButton = document.getElementById('clear-btn');
+ if (clearButton) {
+ clearButton.addEventListener('click', () => {
+ document.getElementById('prompt-input').value = '';
+ document.getElementById('prompt-input').focus();
});
}
});
+