MCP Agent Platform
by rolenet
Verified
- McpAgentRobot
- templates
<!DOCTYPE html>
<html>
<head>
<title>智能体交互界面</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #1a1a2e;
color: #fff;
display: flex;
min-height: 100vh;
}
.robot-face {
width: 50%;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #162447;
}
.robot-head {
width: 300px;
height: 400px;
background: #1f4068;
border-radius: 150px 150px 100px 100px;
position: relative;
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
}
.eyes {
display: flex;
justify-content: space-around;
padding: 80px 20px;
}
.eye {
width: 80px;
height: 80px;
background: #e94560;
border-radius: 50%;
position: relative;
animation: blink 4s infinite;
}
.eye::after {
content: '';
position: absolute;
width: 35px;
height: 35px;
background: #fff;
border-radius: 50%;
top: 20px;
left: 20px;
animation: look 5s infinite;
}
.mouth {
width: 150px;
height: 50px;
background: #e94560;
border-radius: 0 0 50px 50px;
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
animation: talk 1s infinite;
}
.mouth.speaking {
animation: talk 0.3s infinite;
}
@keyframes blink {
0%, 45%, 50%, 55%, 100% {transform: scaleY(1);}
48%, 52% {transform: scaleY(0.1);}
}
@keyframes look {
0%, 100% {transform: translate(0, 0);}
25% {transform: translate(10px, 10px);}
50% {transform: translate(-10px, -10px);}
75% {transform: translate(-10px, 10px);}
}
@keyframes talk {
0%, 100% {height: 50px;}
50% {height: 30px;}
}
.sidebar {
width: 50%;
padding: 20px;
background: #162447;
display: flex;
flex-direction: column;
gap: 20px;
}
.vision-panel {
flex: 1;
background: #1f4068;
border-radius: 10px;
padding: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
#vision-feed {
width: 100%;
height: 300px;
background: #000;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#vision-feed img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.chat-panel {
flex: 1;
background: #1f4068;
border-radius: 10px;
padding: 15px;
display: flex;
flex-direction: column;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.chat-box {
flex: 1;
padding: 10px;
background: rgba(255,255,255,0.1);
border-radius: 5px;
margin-bottom: 10px;
overflow-y: auto;
color: #fff;
}
.chat-box::-webkit-scrollbar {
width: 5px;
}
.chat-box::-webkit-scrollbar-thumb {
background: #e94560;
border-radius: 5px;
}
.input-area {
display: flex;
gap: 10px;
}
#message-input {
flex: 1;
padding: 10px;
border: none;
border-radius: 5px;
background: rgba(255,255,255,0.1);
color: #fff;
}
#message-input::placeholder {
color: rgba(255,255,255,0.5);
}
button {
padding: 10px 20px;
background: #e94560;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #d63651;
}
</style>
</head>
<body>
<div class="robot-face">
<div class="robot-head">
<div class="eyes">
<div class="eye"></div>
<div class="eye"></div>
</div>
<div class="mouth"></div>
</div>
</div>
<div class="sidebar">
<div class="vision-panel">
<div class="sensor-title">👁️ 视觉</div>
<div id="vision-feed"></div>
</div>
<div class="chat-panel">
<div class="chat-box" id="chat-history"></div>
<div class="input-area">
<input type="text" id="message-input" placeholder="输入消息...">
<button onclick="sendMessage()">发送</button>
</div>
</div>
</div>
<script>
let ws = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 10;
let heartbeatInterval = null;
const chatHistory = document.getElementById('chat-history');
const visionFeed = document.getElementById('vision-feed');
const mouth = document.querySelector('.mouth');
// 控制机器人说话动画
function startTalking() {
mouth.classList.add('speaking');
}
function stopTalking() {
mouth.classList.remove('speaking');
}
// 在收到消息时触发说话动画
function handleRobotSpeech(text) {
startTalking();
// 根据文本长度设置说话动画持续时间
const duration = Math.min(text.length * 100, 3000);
setTimeout(stopTalking, duration);
}
function connectWebSocket() {
if (ws && ws.readyState === WebSocket.OPEN) {
console.log('WebSocket已经连接');
return;
}
// 使用当前页面的主机和端口
ws = new WebSocket(`ws://${window.location.host}/ws`);
ws.onopen = function() {
console.log('WebSocket已连接');
reconnectAttempts = 0;
// 设置心跳检测
heartbeatInterval = setInterval(function() {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({type: "heartbeat"}));
}
}, 30000); // 每30秒发送一次心跳
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
switch(data.type) {
case 'vision':
if (data.content) {
const img = new Image();
img.src = `data:image/jpeg;base64,${data.content}`;
img.onerror = function() {
console.error('图像加载失败');
};
img.onload = function() {
visionFeed.innerHTML = '';
visionFeed.appendChild(img);
};
}
break;
case 'chat':
const messageDiv = document.createElement('div');
messageDiv.textContent = `${data.sender_id}: ${data.content.text}`;
chatHistory.appendChild(messageDiv);
chatHistory.scrollTop = chatHistory.scrollHeight;
// 如果是机器人的回复,触发说话动画
if (data.sender_id === 'brain') {
handleRobotSpeech(data.content.text);
}
break;
case 'audio':
// 处理音频消息
if (data.content && data.content.audio_data) {
const audio = new Audio(`data:audio/wav;base64,${data.content.audio_data}`);
audio.play().catch(e => console.error('音频播放失败:', e));
}
break;
}
};
ws.onerror = function(error) {
console.error('WebSocket错误:', error);
};
ws.onclose = function() {
console.log('WebSocket连接关闭');
clearInterval(heartbeatInterval);
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`正在重新连接... 尝试 ${reconnectAttempts}`);
setTimeout(connectWebSocket, 2000);
}
};
}
// Initial connection
connectWebSocket();
function sendMessage() {
const input = document.getElementById('message-input');
const message = input.value.trim();
if (message && ws.readyState === WebSocket.OPEN) {
console.log('Sending message:', message);
ws.send(JSON.stringify({
type: 'text',
sender_id: 'user',
receiver_id: 'brain',
content: {
text: message
}
}));
// 在聊天框中显示发送的消息
const messageDiv = document.createElement('div');
messageDiv.textContent = `User: ${message}`;
chatHistory.appendChild(messageDiv);
chatHistory.scrollTop = chatHistory.scrollHeight;
input.value = '';
}
}
document.getElementById('message-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
</script>
</body>
</html>