NEWORLD / src /static /index.html
Factor Studios
Upload 32 files
55055c7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Chat - Virtual GPU</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.chat-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 800px;
height: 600px;
display: flex;
flex-direction: column;
overflow: hidden;
backdrop-filter: blur(10px);
}
.chat-header {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
padding: 20px;
text-align: center;
position: relative;
}
.chat-header h1 {
font-size: 24px;
margin-bottom: 5px;
}
.chat-header p {
font-size: 14px;
opacity: 0.9;
}
.gpu-status {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.2);
padding: 8px 12px;
border-radius: 20px;
font-size: 12px;
cursor: pointer;
transition: all 0.3s ease;
}
.gpu-status:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
.gpu-status.online {
background: rgba(76, 175, 80, 0.8);
}
.chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 70%;
padding: 12px 18px;
border-radius: 18px;
word-wrap: break-word;
animation: fadeIn 0.3s ease;
}
.message.user {
align-self: flex-end;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.message.ai {
align-self: flex-start;
background: #f1f3f4;
color: #333;
border: 1px solid #e0e0e0;
}
.message.ai .gpu-info {
font-size: 11px;
color: #666;
margin-top: 5px;
font-family: monospace;
}
.chat-input-container {
padding: 20px;
background: #f8f9fa;
border-top: 1px solid #e0e0e0;
display: flex;
gap: 10px;
align-items: center;
}
.chat-input {
flex: 1;
padding: 12px 18px;
border: 2px solid #e0e0e0;
border-radius: 25px;
font-size: 16px;
outline: none;
transition: all 0.3s ease;
}
.chat-input:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.send-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
min-width: 80px;
}
.send-button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.send-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.loading {
display: flex;
align-items: center;
gap: 5px;
color: #666;
font-style: italic;
}
.loading-dots {
display: inline-flex;
gap: 2px;
}
.loading-dots span {
width: 4px;
height: 4px;
background: #666;
border-radius: 50%;
animation: bounce 1.4s ease-in-out infinite both;
}
.loading-dots span:nth-child(1) { animation-delay: -0.32s; }
.loading-dots span:nth-child(2) { animation-delay: -0.16s; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
.gpu-stats-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.gpu-stats-content {
background: white;
padding: 30px;
border-radius: 15px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.gpu-stats-content h3 {
margin-bottom: 15px;
color: #333;
}
.stat-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.stat-item:last-child {
border-bottom: none;
}
.close-modal {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin-top: 20px;
}
@media (max-width: 768px) {
.chat-container {
height: 100vh;
border-radius: 0;
}
.message {
max-width: 85%;
}
.gpu-status {
position: static;
margin-top: 10px;
display: inline-block;
}
}
</style>
</head>
<body>
<div class="chat-container">
<div class="chat-header">
<h1>AI Chat Interface</h1>
<p>Powered by Virtual GPU - 50,000 Cores | 800 SMs | 500GB VRAM</p>
<div class="gpu-status" id="gpuStatus" onclick="showGpuStats()">
GPU: Initializing...
</div>
</div>
<div class="chat-messages" id="chatMessages">
<div class="message ai">
<div>Welcome! I'm an AI running on a simulated GPU with 50,000 virtual cores. Ask me anything!</div>
<div class="gpu-info">Virtual GPU initializing...</div>
</div>
</div>
<div class="chat-input-container">
<input type="text" class="chat-input" id="chatInput" placeholder="Type your message here..." onkeypress="handleKeyPress(event)">
<button class="send-button" id="sendButton" onclick="sendMessage()">Send</button>
</div>
</div>
<!-- GPU Stats Modal -->
<div class="gpu-stats-modal" id="gpuStatsModal">
<div class="gpu-stats-content">
<h3>Virtual GPU Statistics</h3>
<div id="gpuStatsContent">Loading...</div>
<button class="close-modal" onclick="closeGpuStats()">Close</button>
</div>
</div>
<script>
let isLoading = false;
// Initialize GPU status check
updateGpuStatus();
setInterval(updateGpuStatus, 5000); // Update every 5 seconds
function handleKeyPress(event) {
if (event.key === 'Enter' && !isLoading) {
sendMessage();
}
}
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message || isLoading) return;
// Add user message to chat
addMessage(message, 'user');
input.value = '';
// Show loading state
isLoading = true;
document.getElementById('sendButton').disabled = true;
document.getElementById('sendButton').textContent = 'Sending...';
// Add loading message
const loadingId = addLoadingMessage();
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: message })
});
const data = await response.json();
// Remove loading message
removeLoadingMessage(loadingId);
if (data.error) {
addMessage(`Error: ${data.error}`, 'ai');
} else {
addMessage(data.response, 'ai', data.gpu_stats);
}
} catch (error) {
removeLoadingMessage(loadingId);
addMessage(`Connection error: ${error.message}`, 'ai');
} finally {
isLoading = false;
document.getElementById('sendButton').disabled = false;
document.getElementById('sendButton').textContent = 'Send';
}
}
function addMessage(text, sender, gpuStats = null) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
let content = `<div>${text}</div>`;
if (sender === 'ai' && gpuStats) {
content += `<div class="gpu-info">Clock: ${gpuStats.clock_cycles} | Tasks: ${gpuStats.tasks_processed} | SMs: ${gpuStats.busy_sms}/${gpuStats.total_sms} | FLOPs: ${gpuStats.flops_performed.toLocaleString()}</div>`;
}
messageDiv.innerHTML = content;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function addLoadingMessage() {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message ai loading';
messageDiv.id = 'loading-message-' + Date.now();
messageDiv.innerHTML = `
<div>Processing on virtual GPU</div>
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
`;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return messageDiv.id;
}
function removeLoadingMessage(loadingId) {
const loadingMessage = document.getElementById(loadingId);
if (loadingMessage) {
loadingMessage.remove();
}
}
async function updateGpuStatus() {
try {
const response = await fetch('/api/gpu-status');
const data = await response.json();
const statusElement = document.getElementById('gpuStatus');
if (data.error) {
statusElement.textContent = 'GPU: Error';
statusElement.className = 'gpu-status';
} else {
statusElement.textContent = `GPU: Online (${data.vgpu.busy_sms}/${data.vgpu.total_sms} SMs)`;
statusElement.className = 'gpu-status online';
}
} catch (error) {
document.getElementById('gpuStatus').textContent = 'GPU: Offline';
document.getElementById('gpuStatus').className = 'gpu-status';
}
}
async function showGpuStats() {
try {
const response = await fetch('/api/gpu-status');
const data = await response.json();
if (data.error) {
document.getElementById('gpuStatsContent').innerHTML = `<p>Error: ${data.error}</p>`;
} else {
let statsHtml = '<h4>vGPU Core</h4>';
Object.entries(data.vgpu).forEach(([key, value]) => {
statsHtml += `<div class="stat-item"><span>${key.replace(/_/g, ' ')}:</span><span>${typeof value === 'number' ? value.toLocaleString() : value}</span></div>`;
});
statsHtml += '<h4>AI Accelerator</h4>';
Object.entries(data.ai_accelerator).forEach(([key, value]) => {
statsHtml += `<div class="stat-item"><span>${key.replace(/_/g, ' ')}:</span><span>${typeof value === 'number' ? value.toLocaleString() : value}</span></div>`;
});
statsHtml += '<h4>VRAM</h4>';
Object.entries(data.vram).forEach(([key, value]) => {
if (typeof value === 'number') {
if (key.includes('percent')) {
value = value.toFixed(2) + '%';
} else if (key.includes('gb')) {
value = value.toFixed(1) + ' GB';
} else {
value = value.toLocaleString();
}
}
statsHtml += `<div class="stat-item"><span>${key.replace(/_/g, ' ')}:</span><span>${value}</span></div>`;
});
document.getElementById('gpuStatsContent').innerHTML = statsHtml;
}
document.getElementById('gpuStatsModal').style.display = 'flex';
} catch (error) {
document.getElementById('gpuStatsContent').innerHTML = `<p>Connection error: ${error.message}</p>`;
document.getElementById('gpuStatsModal').style.display = 'flex';
}
}
function closeGpuStats() {
document.getElementById('gpuStatsModal').style.display = 'none';
}
// Close modal when clicking outside
document.getElementById('gpuStatsModal').addEventListener('click', function(e) {
if (e.target === this) {
closeGpuStats();
}
});
</script>
</body>
</html>