PowerBot / index.html
OrbitMC's picture
Update index.html
fe7ccd7 verified
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bot Manager</title>
<script src="/socket.io/socket.io.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #000;
color: #fff;
padding: 20px;
line-height: 1.5;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #333;
}
.header h1 {
font-size: 28px;
font-weight: 600;
margin-bottom: 5px;
}
.header p {
color: #888;
font-size: 14px;
}
.card {
background: #111;
border: 1px solid #222;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.card-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 15px;
color: #888;
text-transform: uppercase;
letter-spacing: 1px;
}
.server-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
}
.info-item {
background: #0a0a0a;
padding: 12px;
border-radius: 6px;
border: 1px solid #1a1a1a;
}
.info-label {
font-size: 11px;
color: #666;
margin-bottom: 5px;
}
.info-value {
font-size: 16px;
font-weight: 600;
}
.status-online {
color: #0f0;
}
.status-offline {
color: #f00;
}
.rotation-card {
background: linear-gradient(135deg, #1a1a1a 0%, #0a0a0a 100%);
border: 1px solid #333;
text-align: center;
padding: 30px 20px;
}
.current-bot {
font-size: 36px;
font-weight: 700;
margin: 15px 0;
letter-spacing: 2px;
}
.timer {
font-size: 32px;
font-family: 'Courier New', monospace;
margin: 20px 0;
color: #888;
}
.progress-bar {
background: #1a1a1a;
height: 4px;
border-radius: 2px;
overflow: hidden;
margin: 20px 0;
}
.progress-fill {
background: #fff;
height: 100%;
transition: width 1s linear;
}
.next-info {
font-size: 14px;
color: #666;
margin-top: 15px;
}
button {
background: #fff;
color: #000;
border: none;
padding: 10px 24px;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
margin: 10px 5px 0;
transition: all 0.2s;
}
button:hover {
background: #ddd;
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
.queue {
display: flex;
justify-content: center;
gap: 10px;
flex-wrap: wrap;
margin: 20px 0;
}
.queue-item {
background: #0a0a0a;
padding: 10px 20px;
border-radius: 6px;
border: 1px solid #1a1a1a;
font-size: 13px;
font-weight: 600;
}
.queue-item.active {
background: #fff;
color: #000;
}
.queue-item.next {
border-color: #666;
}
.bot-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.bot-card {
background: #0a0a0a;
border: 1px solid #1a1a1a;
border-radius: 6px;
padding: 15px;
transition: all 0.3s;
}
.bot-card.active {
border-color: #fff;
background: #1a1a1a;
}
.bot-card.online {
border-left: 3px solid #0f0;
}
.bot-card.offline {
border-left: 3px solid #333;
}
.bot-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.bot-name {
font-size: 16px;
font-weight: 600;
}
.bot-badge {
background: #fff;
color: #000;
padding: 3px 8px;
border-radius: 4px;
font-size: 10px;
font-weight: 700;
}
.bot-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin: 12px 0;
}
.stat {
background: #000;
padding: 8px;
border-radius: 4px;
text-align: center;
}
.stat-label {
font-size: 10px;
color: #666;
}
.stat-value {
font-size: 14px;
font-weight: 600;
margin-top: 3px;
}
.bot-status {
text-align: center;
padding: 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
background: #000;
margin-top: 10px;
}
.toast {
position: fixed;
top: 20px;
right: 20px;
background: #fff;
color: #000;
padding: 12px 20px;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
z-index: 1000;
animation: slideIn 0.3s;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.header h1 {
font-size: 22px;
}
.current-bot {
font-size: 24px;
}
.timer {
font-size: 20px;
}
.bot-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎮 BOT MANAGER</h1>
<p>OrbitMC Server Monitor</p>
</div>
<!-- Server Status -->
<div class="card">
<div class="card-title">Server Status</div>
<div class="server-info">
<div class="info-item">
<div class="info-label">Address</div>
<div class="info-value" id="server-addr">-</div>
</div>
<div class="info-item">
<div class="info-label">Status</div>
<div class="info-value" id="server-status">-</div>
</div>
<div class="info-item">
<div class="info-label">Latency</div>
<div class="info-value" id="server-latency">-</div>
</div>
<div class="info-item">
<div class="info-label">Version</div>
<div class="info-value" id="server-version">-</div>
</div>
</div>
</div>
<!-- Rotation -->
<div class="card rotation-card">
<div class="card-title">Current Active Bot</div>
<div class="current-bot" id="current-bot">-</div>
<div class="timer" id="timer">00:00:00</div>
<div class="progress-bar">
<div class="progress-fill" id="progress"></div>
</div>
<div class="next-info">
Next: <strong id="next-bot">-</strong> in <strong id="next-time">-</strong>
</div>
<button onclick="forceRotation()">⏭ Next Bot</button>
</div>
<!-- Queue -->
<div class="card">
<div class="card-title">Rotation Queue</div>
<div class="queue" id="queue"></div>
</div>
<!-- Bots -->
<div class="bot-grid" id="bot-grid"></div>
</div>
<div id="toast-container"></div>
<script>
const socket = io();
let data = {};
socket.on('update', (update) => {
data = update;
render();
});
function render() {
// Server
const srv = data.server;
if (srv) {
document.getElementById('server-addr').textContent = `${srv.host}:${srv.port}`;
const status = document.getElementById('server-status');
status.textContent = srv.status.online ? 'Online' : 'Offline';
status.className = 'info-value ' + (srv.status.online ? 'status-online' : 'status-offline');
document.getElementById('server-latency').textContent = srv.status.online ? `${srv.status.latency}ms` : 'N/A';
document.getElementById('server-version').textContent = srv.version;
}
// Rotation
const rot = data.rotation;
if (rot) {
document.getElementById('current-bot').textContent = rot.current || '-';
document.getElementById('timer').textContent = formatTime(rot.elapsed);
document.getElementById('next-bot').textContent = rot.next || '-';
document.getElementById('next-time').textContent = formatTime(rot.remaining);
const progress = (rot.elapsed / 3600) * 100;
document.getElementById('progress').style.width = progress + '%';
// Queue
const queueEl = document.getElementById('queue');
queueEl.innerHTML = '';
rot.queue.forEach((bot, i) => {
const item = document.createElement('div');
item.className = 'queue-item';
if (bot === rot.current) item.className += ' active';
else if (bot === rot.next) item.className += ' next';
item.textContent = bot;
queueEl.appendChild(item);
});
}
// Bots
const bots = data.bots;
if (bots) {
const grid = document.getElementById('bot-grid');
grid.innerHTML = '';
bots.forEach(bot => {
const card = document.createElement('div');
card.className = `bot-card ${bot.status}`;
if (bot.isActive) card.className += ' active';
const badge = bot.isActive ? '<div class="bot-badge">ACTIVE</div>' : '';
card.innerHTML = `
<div class="bot-header">
<div class="bot-name">${bot.name}</div>
${badge}
</div>
<div class="bot-stats">
<div class="stat">
<div class="stat-label">Uptime</div>
<div class="stat-value">${formatTimeShort(bot.uptimeSeconds || 0)}</div>
</div>
<div class="stat">
<div class="stat-label">Deaths</div>
<div class="stat-value">${bot.deaths}</div>
</div>
<div class="stat">
<div class="stat-label">Position</div>
<div class="stat-value">${bot.position.x}, ${bot.position.y}, ${bot.position.z}</div>
</div>
<div class="stat">
<div class="stat-label">Health</div>
<div class="stat-value">❤️ ${bot.health}</div>
</div>
</div>
<div class="bot-status">${bot.status.toUpperCase()}</div>
`;
grid.appendChild(card);
});
}
}
function formatTime(sec) {
if (!sec) return '00:00:00';
const h = Math.floor(sec / 3600);
const m = Math.floor((sec % 3600) / 60);
const s = sec % 60;
return `${pad(h)}:${pad(m)}:${pad(s)}`;
}
function formatTimeShort(sec) {
if (!sec) return '0m';
const m = Math.floor(sec / 60);
if (m < 60) return `${m}m`;
const h = Math.floor(m / 60);
return `${h}h ${m % 60}m`;
}
function pad(n) {
return n.toString().padStart(2, '0');
}
function forceRotation() {
if (confirm('Switch to next bot?')) {
socket.emit('forceRotation');
toast('Switching to next bot...');
}
}
function toast(msg) {
const container = document.getElementById('toast-container');
const el = document.createElement('div');
el.className = 'toast';
el.textContent = msg;
container.appendChild(el);
setTimeout(() => el.remove(), 3000);
}
socket.on('rotationForced', () => {
toast('✅ Rotation forced');
});
</script>
</body>
</html>