OrbitMC commited on
Commit
fe7ccd7
·
verified ·
1 Parent(s): d4d39bb

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +480 -687
index.html CHANGED
@@ -1,705 +1,498 @@
1
  <!-- index.html -->
2
  <!DOCTYPE html>
3
- <html>
4
  <head>
5
- <title>Bot Manager - OrbitMC</title>
6
- <meta charset="utf-8">
7
- <meta name="viewport" content="width=device-width,initial-scale=1">
8
- <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
- min-height: 100vh;
19
- padding: 20px;
20
- color: #fff;
21
- }
22
-
23
- .container {
24
- max-width: 1400px;
25
- margin: 0 auto;
26
- }
27
-
28
- .header {
29
- text-align: center;
30
- margin-bottom: 30px;
31
- }
32
-
33
- .header h1 {
34
- font-size: 42px;
35
- margin-bottom: 10px;
36
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
37
- }
38
-
39
- .header p {
40
- font-size: 18px;
41
- opacity: 0.9;
42
- }
43
-
44
- .server-card {
45
- background: rgba(255, 255, 255, 0.1);
46
- backdrop-filter: blur(10px);
47
- border-radius: 20px;
48
- padding: 30px;
49
- margin-bottom: 25px;
50
- border: 1px solid rgba(255, 255, 255, 0.2);
51
- box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
52
- }
53
-
54
- .server-header {
55
- display: flex;
56
- justify-content: space-between;
57
- align-items: center;
58
- margin-bottom: 20px;
59
- }
60
-
61
- .server-title {
62
- font-size: 24px;
63
- font-weight: bold;
64
- }
65
-
66
- .status-badge {
67
- padding: 8px 20px;
68
- border-radius: 20px;
69
- font-weight: bold;
70
- font-size: 14px;
71
- display: inline-flex;
72
- align-items: center;
73
- gap: 8px;
74
- }
75
-
76
- .status-badge.online {
77
- background: #10b981;
78
- animation: pulse 2s infinite;
79
- }
80
-
81
- .status-badge.offline {
82
- background: #ef4444;
83
- }
84
-
85
- @keyframes pulse {
86
- 0%, 100% { opacity: 1; }
87
- 50% { opacity: 0.7; }
88
- }
89
-
90
- .status-dot {
91
- width: 10px;
92
- height: 10px;
93
- border-radius: 50%;
94
- background: white;
95
- animation: blink 1s infinite;
96
- }
97
-
98
- @keyframes blink {
99
- 0%, 100% { opacity: 1; }
100
- 50% { opacity: 0.3; }
101
- }
102
-
103
- .server-info {
104
- display: grid;
105
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
106
- gap: 15px;
107
- }
108
-
109
- .info-item {
110
- background: rgba(255, 255, 255, 0.1);
111
- padding: 15px;
112
- border-radius: 10px;
113
- text-align: center;
114
- }
115
-
116
- .info-label {
117
- font-size: 12px;
118
- opacity: 0.8;
119
- margin-bottom: 5px;
120
- }
121
-
122
- .info-value {
123
- font-size: 20px;
124
- font-weight: bold;
125
- }
126
-
127
- .rotation-card {
128
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
129
- border-radius: 20px;
130
- padding: 30px;
131
- margin-bottom: 25px;
132
- box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
133
- text-align: center;
134
- }
135
-
136
- .rotation-title {
137
- font-size: 18px;
138
- opacity: 0.9;
139
- margin-bottom: 15px;
140
- }
141
-
142
- .current-bot {
143
- font-size: 48px;
144
- font-weight: bold;
145
- margin: 15px 0;
146
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
147
- }
148
-
149
- .rotation-timer {
150
- font-size: 36px;
151
- font-weight: bold;
152
- margin: 20px 0;
153
- font-family: 'Courier New', monospace;
154
- }
155
-
156
- .progress-bar {
157
- background: rgba(255, 255, 255, 0.3);
158
- border-radius: 10px;
159
- height: 20px;
160
- overflow: hidden;
161
- margin: 20px 0;
162
- }
163
-
164
- .progress-fill {
165
- background: rgba(255, 255, 255, 0.9);
166
- height: 100%;
167
- border-radius: 10px;
168
- transition: width 1s linear;
169
- }
170
-
171
- .next-bot-info {
172
- background: rgba(255, 255, 255, 0.2);
173
- padding: 15px;
174
- border-radius: 10px;
175
- margin-top: 15px;
176
- font-size: 16px;
177
- }
178
-
179
- .controls {
180
- display: flex;
181
- justify-content: center;
182
- gap: 10px;
183
- margin-top: 20px;
184
- }
185
-
186
- button {
187
- background: rgba(255, 255, 255, 0.3);
188
- border: 2px solid rgba(255, 255, 255, 0.5);
189
- color: white;
190
- padding: 12px 30px;
191
- border-radius: 25px;
192
- cursor: pointer;
193
- font-size: 16px;
194
- font-weight: bold;
195
- transition: all 0.3s;
196
- }
197
-
198
- button:hover {
199
- background: rgba(255, 255, 255, 0.5);
200
- transform: translateY(-2px);
201
- box-shadow: 0 5px 15px rgba(0,0,0,0.3);
202
- }
203
-
204
- button:active {
205
- transform: translateY(0);
206
- }
207
-
208
- button.primary {
209
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
210
- border: none;
211
- }
212
-
213
- button.primary:hover {
214
- background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
215
- }
216
-
217
- .queue-section {
218
- background: rgba(255, 255, 255, 0.1);
219
- backdrop-filter: blur(10px);
220
- border-radius: 20px;
221
- padding: 25px;
222
- margin-bottom: 25px;
223
- border: 1px solid rgba(255, 255, 255, 0.2);
224
- }
225
-
226
- .queue-title {
227
- font-size: 20px;
228
- font-weight: bold;
229
- margin-bottom: 20px;
230
- text-align: center;
231
- }
232
-
233
- .queue-list {
234
- display: flex;
235
- justify-content: center;
236
- align-items: center;
237
- gap: 15px;
238
- flex-wrap: wrap;
239
- }
240
-
241
- .queue-item {
242
- background: rgba(255, 255, 255, 0.1);
243
- padding: 15px 25px;
244
- border-radius: 15px;
245
- font-weight: bold;
246
- transition: all 0.3s;
247
- border: 2px solid transparent;
248
- }
249
-
250
- .queue-item.active {
251
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
252
- transform: scale(1.15);
253
- box-shadow: 0 5px 20px rgba(240, 147, 251, 0.4);
254
- }
255
-
256
- .queue-item.next {
257
- border-color: rgba(245, 158, 11, 0.6);
258
- background: rgba(245, 158, 11, 0.2);
259
- }
260
-
261
- .queue-arrow {
262
- font-size: 24px;
263
- opacity: 0.5;
264
- }
265
-
266
- .bot-grid {
267
- display: grid;
268
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
269
- gap: 20px;
270
- margin-bottom: 30px;
271
- }
272
-
273
- .bot-card {
274
- background: rgba(255, 255, 255, 0.1);
275
- backdrop-filter: blur(10px);
276
- border-radius: 15px;
277
- padding: 20px;
278
- border: 2px solid rgba(255, 255, 255, 0.2);
279
- transition: all 0.3s;
280
- position: relative;
281
- overflow: hidden;
282
- }
283
-
284
- .bot-card::before {
285
- content: '';
286
- position: absolute;
287
- top: 0;
288
- left: 0;
289
- right: 0;
290
- height: 4px;
291
- background: #6b7280;
292
- }
293
-
294
- .bot-card.active::before {
295
- background: linear-gradient(90deg, #f093fb 0%, #f5576c 100%);
296
- animation: shimmer 2s infinite;
297
- }
298
-
299
- .bot-card.online::before {
300
- background: #10b981;
301
- }
302
-
303
- .bot-card.connecting::before {
304
- background: #f59e0b;
305
- }
306
-
307
- .bot-card.offline::before {
308
- background: #6b7280;
309
- }
310
-
311
- @keyframes shimmer {
312
- 0% { transform: translateX(-100%); }
313
- 100% { transform: translateX(100%); }
314
- }
315
-
316
- .bot-card.active {
317
- border-color: rgba(240, 147, 251, 0.5);
318
- background: rgba(240, 147, 251, 0.15);
319
- transform: scale(1.05);
320
- box-shadow: 0 10px 30px rgba(240, 147, 251, 0.3);
321
- }
322
-
323
- .bot-header {
324
- display: flex;
325
- justify-content: space-between;
326
- align-items: center;
327
- margin-bottom: 15px;
328
- }
329
-
330
- .bot-name {
331
- font-size: 20px;
332
- font-weight: bold;
333
- }
334
-
335
- .active-badge {
336
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
337
- padding: 4px 12px;
338
- border-radius: 12px;
339
- font-size: 11px;
340
- font-weight: bold;
341
- }
342
-
343
- .bot-stats {
344
- display: grid;
345
- grid-template-columns: 1fr 1fr;
346
- gap: 10px;
347
- margin: 15px 0;
348
- }
349
-
350
- .stat-item {
351
- background: rgba(255, 255, 255, 0.1);
352
- padding: 10px;
353
- border-radius: 8px;
354
- text-align: center;
355
- }
356
-
357
- .stat-label {
358
- font-size: 11px;
359
- opacity: 0.7;
360
- margin-bottom: 3px;
361
- }
362
-
363
- .stat-value {
364
- font-size: 18px;
365
- font-weight: bold;
366
- }
367
-
368
- .bot-status-text {
369
- text-align: center;
370
- padding: 8px;
371
- border-radius: 8px;
372
- font-weight: bold;
373
- margin-top: 10px;
374
- }
375
-
376
- .bot-status-text.online {
377
- background: rgba(16, 185, 129, 0.3);
378
- color: #10b981;
379
- }
380
-
381
- .bot-status-text.offline {
382
- background: rgba(107, 114, 128, 0.3);
383
- color: #9ca3af;
384
- }
385
-
386
- .bot-status-text.connecting {
387
- background: rgba(245, 158, 11, 0.3);
388
- color: #f59e0b;
389
- }
390
-
391
- .bot-status-text.active {
392
- background: rgba(240, 147, 251, 0.3);
393
- color: #f093fb;
394
- }
395
-
396
- .health-bar {
397
- display: flex;
398
- gap: 5px;
399
- margin-top: 10px;
400
- }
401
-
402
- .health-item {
403
- background: rgba(255, 255, 255, 0.1);
404
- padding: 8px;
405
- border-radius: 8px;
406
- flex: 1;
407
- text-align: center;
408
- }
409
-
410
- .health-item small {
411
- display: block;
412
- font-size: 10px;
413
- opacity: 0.7;
414
- }
415
-
416
- .health-item strong {
417
- display: block;
418
- font-size: 16px;
419
- margin-top: 3px;
420
- }
421
-
422
- .msg {
423
- position: fixed;
424
- top: 20px;
425
- right: 20px;
426
- background: #10b981;
427
- color: white;
428
- padding: 15px 25px;
429
- border-radius: 10px;
430
- box-shadow: 0 5px 20px rgba(0,0,0,0.3);
431
- z-index: 1000;
432
- animation: slideIn 0.3s;
433
- }
434
-
435
- .msg.error {
436
- background: #ef4444;
437
- }
438
-
439
- @keyframes slideIn {
440
- from {
441
- transform: translateX(400px);
442
- opacity: 0;
443
- }
444
- to {
445
- transform: translateX(0);
446
- opacity: 1;
447
- }
448
- }
449
-
450
- @media (max-width: 768px) {
451
- .header h1 { font-size: 28px; }
452
- .current-bot { font-size: 32px; }
453
- .rotation-timer { font-size: 24px; }
454
- .bot-grid { grid-template-columns: 1fr; }
455
- }
456
- </style>
457
  </head>
458
  <body>
459
- <div class="container">
460
- <div class="header">
461
- <h1>🎮 Minecraft Bot Manager</h1>
462
- <p>OrbitMC Server Monitor & Bot Rotation System</p>
463
- </div>
464
 
465
- <div class="server-card">
466
- <div class="server-header">
467
- <div class="server-title">📡 Server Status</div>
468
- <div class="status-badge" id="server-badge">
469
- <div class="status-dot"></div>
470
- <span id="server-status-text">Checking...</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  </div>
472
  </div>
473
- <div class="server-info">
474
- <div class="info-item">
475
- <div class="info-label">Address</div>
476
- <div class="info-value" id="server-address">-</div>
477
- </div>
478
- <div class="info-item">
479
- <div class="info-label">Players Online</div>
480
- <div class="info-value" id="server-players">-</div>
481
- </div>
482
- <div class="info-item">
483
- <div class="info-label">Latency</div>
484
- <div class="info-value" id="server-latency">-</div>
485
  </div>
486
- <div class="info-item">
487
- <div class="info-label">Version</div>
488
- <div class="info-value" id="server-version">-</div>
489
  </div>
 
490
  </div>
491
- </div>
492
 
493
- <div class="rotation-card">
494
- <div class="rotation-title">🔄 CURRENT ACTIVE BOT</div>
495
- <div class="current-bot" id="current-bot">-</div>
496
- <div class="rotation-timer" id="rotation-timer">00:00:00</div>
497
-
498
- <div class="progress-bar">
499
- <div class="progress-fill" id="progress-fill"></div>
500
- </div>
501
-
502
- <div class="next-bot-info">
503
- <strong>Next Bot:</strong> <span id="next-bot">-</span> in <span id="time-until-next">-</span>
504
  </div>
505
-
506
- <div class="controls">
507
- <button class="primary" onclick="forceRotation()">⏭️ Switch to Next Bot</button>
508
- <button onclick="refreshStatus()">🔄 Refresh</button>
509
- </div>
510
- </div>
511
 
512
- <div class="queue-section">
513
- <div class="queue-title">📋 Rotation Queue</div>
514
- <div class="queue-list" id="queue-list"></div>
515
  </div>
516
 
517
- <div class="bot-grid" id="bot-grid"></div>
518
-
519
- <div id="msg-container"></div>
520
- </div>
521
-
522
- <script>
523
- let updateInterval;
524
-
525
- async function fetchStatus() {
526
- try {
527
- const r = await fetch('/api/status');
528
- const data = await r.json();
529
- renderStatus(data);
530
- } catch(e) {
531
- console.error('Failed to fetch status', e);
532
- }
533
- }
534
-
535
- function renderStatus(data) {
536
- const server = data.server_status;
537
- const badge = document.getElementById('server-badge');
538
-
539
- if (server.online) {
540
- badge.className = 'status-badge online';
541
- document.getElementById('server-status-text').textContent = 'Online';
542
- } else {
543
- badge.className = 'status-badge offline';
544
- document.getElementById('server-status-text').textContent = 'Offline';
545
- }
546
-
547
- document.getElementById('server-address').textContent = `${data.server_info.host}:${data.server_info.port}`;
548
- document.getElementById('server-players').textContent = server.players;
549
- document.getElementById('server-latency').textContent = server.latency > 0 ? `${server.latency}ms` : 'N/A';
550
- document.getElementById('server-version').textContent = data.server_info.version;
551
-
552
- const rotation = data.rotation;
553
- document.getElementById('current-bot').textContent = rotation.current_bot || '-';
554
- document.getElementById('rotation-timer').textContent = formatTime(rotation.elapsed);
555
- document.getElementById('next-bot').textContent = rotation.next_bot || '-';
556
- document.getElementById('time-until-next').textContent = formatTime(rotation.remaining);
557
-
558
- const progress = (rotation.elapsed / 3600) * 100;
559
- document.getElementById('progress-fill').style.width = progress + '%';
560
-
561
- renderQueue(rotation.queue, rotation.current_bot, rotation.next_bot);
562
- renderBots(data.bots);
563
- }
564
-
565
- function renderQueue(queue, current, next) {
566
- const queueList = document.getElementById('queue-list');
567
- queueList.innerHTML = '';
568
-
569
- queue.forEach((bot, index) => {
570
- const item = document.createElement('div');
571
- item.className = 'queue-item';
572
-
573
- if (bot === current) {
574
- item.className += ' active';
575
- } else if (bot === next) {
576
- item.className += ' next';
577
- }
578
-
579
- item.textContent = bot;
580
- queueList.appendChild(item);
581
-
582
- if (index < queue.length - 1) {
583
- const arrow = document.createElement('div');
584
- arrow.className = 'queue-arrow';
585
- arrow.textContent = '→';
586
- queueList.appendChild(arrow);
587
- }
588
- });
589
- }
590
-
591
- function renderBots(bots) {
592
- const grid = document.getElementById('bot-grid');
593
- grid.innerHTML = '';
594
-
595
- bots.forEach(bot => {
596
- const card = document.createElement('div');
597
- card.className = `bot-card ${bot.status}`;
598
-
599
- if (bot.is_active) {
600
- card.className += ' active';
601
- }
602
-
603
- const activeBadge = bot.is_active ? '<div class="active-badge">⚡ ACTIVE NOW</div>' : '';
604
-
605
- const statusClass = bot.is_active ? 'active' : bot.status;
606
- const statusText = bot.is_active ? '⚡ ACTIVE & ONLINE' : bot.status.toUpperCase();
607
-
608
- const pos = bot.position;
609
-
610
- card.innerHTML = `
611
- <div class="bot-header">
612
- <div class="bot-name">${bot.name}</div>
613
- ${activeBadge}
614
- </div>
615
- <div class="bot-stats">
616
- <div class="stat-item">
617
- <div class="stat-label">Uptime</div>
618
- <div class="stat-value">${formatTimeShort(bot.uptime)}</div>
619
- </div>
620
- <div class="stat-item">
621
- <div class="stat-label">Deaths</div>
622
- <div class="stat-value">${bot.deaths}</div>
623
- </div>
624
- <div class="stat-item">
625
- <div class="stat-label">Reconnects</div>
626
- <div class="stat-value">${bot.reconnects}</div>
627
- </div>
628
- <div class="stat-item">
629
- <div class="stat-label">Position</div>
630
- <div class="stat-value">${pos.x}, ${pos.y}, ${pos.z}</div>
631
- </div>
632
- </div>
633
- <div class="health-bar">
634
- <div class="health-item">
635
- <small>❤️ Health</small>
636
- <strong>${bot.health}</strong>
637
- </div>
638
- <div class="health-item">
639
- <small>🍖 Food</small>
640
- <strong>${bot.food}</strong>
641
- </div>
642
- </div>
643
- <div class="bot-status-text ${statusClass}">${statusText}</div>
644
- `;
645
-
646
- grid.appendChild(card);
647
- });
648
- }
649
-
650
- function formatTime(seconds) {
651
- if (!seconds || seconds < 0) return '00:00:00';
652
- const h = Math.floor(seconds / 3600);
653
- const m = Math.floor((seconds % 3600) / 60);
654
- const s = seconds % 60;
655
- return `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;
656
- }
657
-
658
- function formatTimeShort(seconds) {
659
- if (!seconds || seconds < 0) return '0m';
660
- const h = Math.floor(seconds / 3600);
661
- const m = Math.floor((seconds % 3600) / 60);
662
- if (h > 0) return `${h}h ${m}m`;
663
- return `${m}m`;
664
- }
665
-
666
- async function forceRotation() {
667
- if (!confirm('Switch to the next bot now?')) return;
668
-
669
- try {
670
- const r = await fetch('/api/next_rotation', {method: 'POST'});
671
- const res = await r.json();
672
-
673
- if (res.success) {
674
- showMsg('✅ Switching to next bot...', 'success');
675
- setTimeout(fetchStatus, 1000);
676
- } else {
677
- showMsg('❌ Failed to rotate', 'error');
678
- }
679
- } catch(e) {
680
- showMsg('❌ Failed to switch bot', 'error');
681
- }
682
- }
683
-
684
- function refreshStatus() {
685
- fetchStatus();
686
- showMsg('🔄 Refreshing...', 'success');
687
- }
688
-
689
- function showMsg(text, type = 'success') {
690
- const container = document.getElementById('msg-container');
691
- const msg = document.createElement('div');
692
- msg.className = 'msg' + (type === 'error' ? ' error' : '');
693
- msg.textContent = text;
694
- container.appendChild(msg);
695
-
696
- setTimeout(() => {
697
- msg.remove();
698
- }, 3000);
699
- }
700
-
701
- fetchStatus();
702
- updateInterval = setInterval(fetchStatus, 1000);
703
- </script>
704
  </body>
705
  </html>
 
1
  <!-- index.html -->
2
  <!DOCTYPE html>
3
+ <html lang="en">
4
  <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Bot Manager</title>
8
+ <script src="/socket.io/socket.io.js"></script>
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
18
+ background: #000;
19
+ color: #fff;
20
+ padding: 20px;
21
+ line-height: 1.5;
22
+ }
23
+
24
+ .container {
25
+ max-width: 1200px;
26
+ margin: 0 auto;
27
+ }
28
+
29
+ .header {
30
+ text-align: center;
31
+ margin-bottom: 30px;
32
+ padding-bottom: 20px;
33
+ border-bottom: 1px solid #333;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 28px;
38
+ font-weight: 600;
39
+ margin-bottom: 5px;
40
+ }
41
+
42
+ .header p {
43
+ color: #888;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .card {
48
+ background: #111;
49
+ border: 1px solid #222;
50
+ border-radius: 8px;
51
+ padding: 20px;
52
+ margin-bottom: 20px;
53
+ }
54
+
55
+ .card-title {
56
+ font-size: 14px;
57
+ font-weight: 600;
58
+ margin-bottom: 15px;
59
+ color: #888;
60
+ text-transform: uppercase;
61
+ letter-spacing: 1px;
62
+ }
63
+
64
+ .server-info {
65
+ display: grid;
66
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
67
+ gap: 15px;
68
+ }
69
+
70
+ .info-item {
71
+ background: #0a0a0a;
72
+ padding: 12px;
73
+ border-radius: 6px;
74
+ border: 1px solid #1a1a1a;
75
+ }
76
+
77
+ .info-label {
78
+ font-size: 11px;
79
+ color: #666;
80
+ margin-bottom: 5px;
81
+ }
82
+
83
+ .info-value {
84
+ font-size: 16px;
85
+ font-weight: 600;
86
+ }
87
+
88
+ .status-online {
89
+ color: #0f0;
90
+ }
91
+
92
+ .status-offline {
93
+ color: #f00;
94
+ }
95
+
96
+ .rotation-card {
97
+ background: linear-gradient(135deg, #1a1a1a 0%, #0a0a0a 100%);
98
+ border: 1px solid #333;
99
+ text-align: center;
100
+ padding: 30px 20px;
101
+ }
102
+
103
+ .current-bot {
104
+ font-size: 36px;
105
+ font-weight: 700;
106
+ margin: 15px 0;
107
+ letter-spacing: 2px;
108
+ }
109
+
110
+ .timer {
111
+ font-size: 32px;
112
+ font-family: 'Courier New', monospace;
113
+ margin: 20px 0;
114
+ color: #888;
115
+ }
116
+
117
+ .progress-bar {
118
+ background: #1a1a1a;
119
+ height: 4px;
120
+ border-radius: 2px;
121
+ overflow: hidden;
122
+ margin: 20px 0;
123
+ }
124
+
125
+ .progress-fill {
126
+ background: #fff;
127
+ height: 100%;
128
+ transition: width 1s linear;
129
+ }
130
+
131
+ .next-info {
132
+ font-size: 14px;
133
+ color: #666;
134
+ margin-top: 15px;
135
+ }
136
+
137
+ button {
138
+ background: #fff;
139
+ color: #000;
140
+ border: none;
141
+ padding: 10px 24px;
142
+ border-radius: 6px;
143
+ font-size: 14px;
144
+ font-weight: 600;
145
+ cursor: pointer;
146
+ margin: 10px 5px 0;
147
+ transition: all 0.2s;
148
+ }
149
+
150
+ button:hover {
151
+ background: #ddd;
152
+ transform: translateY(-1px);
153
+ }
154
+
155
+ button:active {
156
+ transform: translateY(0);
157
+ }
158
+
159
+ .queue {
160
+ display: flex;
161
+ justify-content: center;
162
+ gap: 10px;
163
+ flex-wrap: wrap;
164
+ margin: 20px 0;
165
+ }
166
+
167
+ .queue-item {
168
+ background: #0a0a0a;
169
+ padding: 10px 20px;
170
+ border-radius: 6px;
171
+ border: 1px solid #1a1a1a;
172
+ font-size: 13px;
173
+ font-weight: 600;
174
+ }
175
+
176
+ .queue-item.active {
177
+ background: #fff;
178
+ color: #000;
179
+ }
180
+
181
+ .queue-item.next {
182
+ border-color: #666;
183
+ }
184
+
185
+ .bot-grid {
186
+ display: grid;
187
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
188
+ gap: 15px;
189
+ }
190
+
191
+ .bot-card {
192
+ background: #0a0a0a;
193
+ border: 1px solid #1a1a1a;
194
+ border-radius: 6px;
195
+ padding: 15px;
196
+ transition: all 0.3s;
197
+ }
198
+
199
+ .bot-card.active {
200
+ border-color: #fff;
201
+ background: #1a1a1a;
202
+ }
203
+
204
+ .bot-card.online {
205
+ border-left: 3px solid #0f0;
206
+ }
207
+
208
+ .bot-card.offline {
209
+ border-left: 3px solid #333;
210
+ }
211
+
212
+ .bot-header {
213
+ display: flex;
214
+ justify-content: space-between;
215
+ align-items: center;
216
+ margin-bottom: 12px;
217
+ }
218
+
219
+ .bot-name {
220
+ font-size: 16px;
221
+ font-weight: 600;
222
+ }
223
+
224
+ .bot-badge {
225
+ background: #fff;
226
+ color: #000;
227
+ padding: 3px 8px;
228
+ border-radius: 4px;
229
+ font-size: 10px;
230
+ font-weight: 700;
231
+ }
232
+
233
+ .bot-stats {
234
+ display: grid;
235
+ grid-template-columns: 1fr 1fr;
236
+ gap: 8px;
237
+ margin: 12px 0;
238
+ }
239
+
240
+ .stat {
241
+ background: #000;
242
+ padding: 8px;
243
+ border-radius: 4px;
244
+ text-align: center;
245
+ }
246
+
247
+ .stat-label {
248
+ font-size: 10px;
249
+ color: #666;
250
+ }
251
+
252
+ .stat-value {
253
+ font-size: 14px;
254
+ font-weight: 600;
255
+ margin-top: 3px;
256
+ }
257
+
258
+ .bot-status {
259
+ text-align: center;
260
+ padding: 8px;
261
+ border-radius: 4px;
262
+ font-size: 12px;
263
+ font-weight: 600;
264
+ background: #000;
265
+ margin-top: 10px;
266
+ }
267
+
268
+ .toast {
269
+ position: fixed;
270
+ top: 20px;
271
+ right: 20px;
272
+ background: #fff;
273
+ color: #000;
274
+ padding: 12px 20px;
275
+ border-radius: 6px;
276
+ font-size: 14px;
277
+ font-weight: 600;
278
+ z-index: 1000;
279
+ animation: slideIn 0.3s;
280
+ }
281
+
282
+ @keyframes slideIn {
283
+ from {
284
+ transform: translateX(400px);
285
+ opacity: 0;
286
+ }
287
+ to {
288
+ transform: translateX(0);
289
+ opacity: 1;
290
+ }
291
+ }
292
+
293
+ @media (max-width: 768px) {
294
+ body {
295
+ padding: 10px;
296
+ }
297
+ .header h1 {
298
+ font-size: 22px;
299
+ }
300
+ .current-bot {
301
+ font-size: 24px;
302
+ }
303
+ .timer {
304
+ font-size: 20px;
305
+ }
306
+ .bot-grid {
307
+ grid-template-columns: 1fr;
308
+ }
309
+ }
310
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  </head>
312
  <body>
313
+ <div class="container">
314
+ <div class="header">
315
+ <h1>🎮 BOT MANAGER</h1>
316
+ <p>OrbitMC Server Monitor</p>
317
+ </div>
318
 
319
+ <!-- Server Status -->
320
+ <div class="card">
321
+ <div class="card-title">Server Status</div>
322
+ <div class="server-info">
323
+ <div class="info-item">
324
+ <div class="info-label">Address</div>
325
+ <div class="info-value" id="server-addr">-</div>
326
+ </div>
327
+ <div class="info-item">
328
+ <div class="info-label">Status</div>
329
+ <div class="info-value" id="server-status">-</div>
330
+ </div>
331
+ <div class="info-item">
332
+ <div class="info-label">Latency</div>
333
+ <div class="info-value" id="server-latency">-</div>
334
+ </div>
335
+ <div class="info-item">
336
+ <div class="info-label">Version</div>
337
+ <div class="info-value" id="server-version">-</div>
338
+ </div>
339
  </div>
340
  </div>
341
+
342
+ <!-- Rotation -->
343
+ <div class="card rotation-card">
344
+ <div class="card-title">Current Active Bot</div>
345
+ <div class="current-bot" id="current-bot">-</div>
346
+ <div class="timer" id="timer">00:00:00</div>
347
+ <div class="progress-bar">
348
+ <div class="progress-fill" id="progress"></div>
 
 
 
 
349
  </div>
350
+ <div class="next-info">
351
+ Next: <strong id="next-bot">-</strong> in <strong id="next-time">-</strong>
 
352
  </div>
353
+ <button onclick="forceRotation()">⏭ Next Bot</button>
354
  </div>
 
355
 
356
+ <!-- Queue -->
357
+ <div class="card">
358
+ <div class="card-title">Rotation Queue</div>
359
+ <div class="queue" id="queue"></div>
 
 
 
 
 
 
 
360
  </div>
 
 
 
 
 
 
361
 
362
+ <!-- Bots -->
363
+ <div class="bot-grid" id="bot-grid"></div>
 
364
  </div>
365
 
366
+ <div id="toast-container"></div>
367
+
368
+ <script>
369
+ const socket = io();
370
+ let data = {};
371
+
372
+ socket.on('update', (update) => {
373
+ data = update;
374
+ render();
375
+ });
376
+
377
+ function render() {
378
+ // Server
379
+ const srv = data.server;
380
+ if (srv) {
381
+ document.getElementById('server-addr').textContent = `${srv.host}:${srv.port}`;
382
+ const status = document.getElementById('server-status');
383
+ status.textContent = srv.status.online ? 'Online' : 'Offline';
384
+ status.className = 'info-value ' + (srv.status.online ? 'status-online' : 'status-offline');
385
+ document.getElementById('server-latency').textContent = srv.status.online ? `${srv.status.latency}ms` : 'N/A';
386
+ document.getElementById('server-version').textContent = srv.version;
387
+ }
388
+
389
+ // Rotation
390
+ const rot = data.rotation;
391
+ if (rot) {
392
+ document.getElementById('current-bot').textContent = rot.current || '-';
393
+ document.getElementById('timer').textContent = formatTime(rot.elapsed);
394
+ document.getElementById('next-bot').textContent = rot.next || '-';
395
+ document.getElementById('next-time').textContent = formatTime(rot.remaining);
396
+
397
+ const progress = (rot.elapsed / 3600) * 100;
398
+ document.getElementById('progress').style.width = progress + '%';
399
+
400
+ // Queue
401
+ const queueEl = document.getElementById('queue');
402
+ queueEl.innerHTML = '';
403
+ rot.queue.forEach((bot, i) => {
404
+ const item = document.createElement('div');
405
+ item.className = 'queue-item';
406
+ if (bot === rot.current) item.className += ' active';
407
+ else if (bot === rot.next) item.className += ' next';
408
+ item.textContent = bot;
409
+ queueEl.appendChild(item);
410
+ });
411
+ }
412
+
413
+ // Bots
414
+ const bots = data.bots;
415
+ if (bots) {
416
+ const grid = document.getElementById('bot-grid');
417
+ grid.innerHTML = '';
418
+
419
+ bots.forEach(bot => {
420
+ const card = document.createElement('div');
421
+ card.className = `bot-card ${bot.status}`;
422
+ if (bot.isActive) card.className += ' active';
423
+
424
+ const badge = bot.isActive ? '<div class="bot-badge">ACTIVE</div>' : '';
425
+
426
+ card.innerHTML = `
427
+ <div class="bot-header">
428
+ <div class="bot-name">${bot.name}</div>
429
+ ${badge}
430
+ </div>
431
+ <div class="bot-stats">
432
+ <div class="stat">
433
+ <div class="stat-label">Uptime</div>
434
+ <div class="stat-value">${formatTimeShort(bot.uptimeSeconds || 0)}</div>
435
+ </div>
436
+ <div class="stat">
437
+ <div class="stat-label">Deaths</div>
438
+ <div class="stat-value">${bot.deaths}</div>
439
+ </div>
440
+ <div class="stat">
441
+ <div class="stat-label">Position</div>
442
+ <div class="stat-value">${bot.position.x}, ${bot.position.y}, ${bot.position.z}</div>
443
+ </div>
444
+ <div class="stat">
445
+ <div class="stat-label">Health</div>
446
+ <div class="stat-value">❤️ ${bot.health}</div>
447
+ </div>
448
+ </div>
449
+ <div class="bot-status">${bot.status.toUpperCase()}</div>
450
+ `;
451
+
452
+ grid.appendChild(card);
453
+ });
454
+ }
455
+ }
456
+
457
+ function formatTime(sec) {
458
+ if (!sec) return '00:00:00';
459
+ const h = Math.floor(sec / 3600);
460
+ const m = Math.floor((sec % 3600) / 60);
461
+ const s = sec % 60;
462
+ return `${pad(h)}:${pad(m)}:${pad(s)}`;
463
+ }
464
+
465
+ function formatTimeShort(sec) {
466
+ if (!sec) return '0m';
467
+ const m = Math.floor(sec / 60);
468
+ if (m < 60) return `${m}m`;
469
+ const h = Math.floor(m / 60);
470
+ return `${h}h ${m % 60}m`;
471
+ }
472
+
473
+ function pad(n) {
474
+ return n.toString().padStart(2, '0');
475
+ }
476
+
477
+ function forceRotation() {
478
+ if (confirm('Switch to next bot?')) {
479
+ socket.emit('forceRotation');
480
+ toast('Switching to next bot...');
481
+ }
482
+ }
483
+
484
+ function toast(msg) {
485
+ const container = document.getElementById('toast-container');
486
+ const el = document.createElement('div');
487
+ el.className = 'toast';
488
+ el.textContent = msg;
489
+ container.appendChild(el);
490
+ setTimeout(() => el.remove(), 3000);
491
+ }
492
+
493
+ socket.on('rotationForced', () => {
494
+ toast('✅ Rotation forced');
495
+ });
496
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  </body>
498
  </html>