srinetwork commited on
Commit
60badcc
·
verified ·
1 Parent(s): aa2b8b4

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +273 -25
Dockerfile CHANGED
@@ -80,7 +80,7 @@ echo "✅ Auto-backup enabled (every 5 minutes)"
80
  echo ""
81
 
82
  # ==========================================
83
- # WEB TERMINAL SERVER (Fixed Uptime + Persistent Logs)
84
  # ==========================================
85
  cat > /app/web_terminal.py << 'PYTHON_SCRIPT'
86
  import os
@@ -120,7 +120,6 @@ def load_logs_from_file():
120
  if os.path.exists(LOGS_FILE):
121
  with open(LOGS_FILE, 'r', encoding='utf-8') as f:
122
  logs_history = f.read().splitlines()
123
- # Keep only last 1000 lines
124
  logs_history = logs_history[-1000:]
125
  print(f"[LOGS] Loaded {len(logs_history)} lines from file", flush=True)
126
  except Exception as e:
@@ -141,22 +140,18 @@ def push_logs_to_github():
141
  try:
142
  if not GITHUB_TOKEN:
143
  return
144
-
145
- # Read current logs
146
  if not os.path.exists(LOGS_FILE):
147
  return
148
 
149
  with open(LOGS_FILE, 'r', encoding='utf-8') as f:
150
  content = f.read()
151
 
152
- # GitHub API - get current file SHA
153
  api_url = f"https://api.github.com/repos/{GITHUB_REPO}/contents/logs/console_logs.txt"
154
  headers = {
155
  "Authorization": f"token {GITHUB_TOKEN}",
156
  "Accept": "application/vnd.github.v3+json"
157
  }
158
 
159
- # Check if file exists
160
  sha = None
161
  try:
162
  resp = requests.get(api_url, headers=headers, timeout=10)
@@ -165,7 +160,6 @@ def push_logs_to_github():
165
  except:
166
  pass
167
 
168
- # Create or update file
169
  import base64
170
  encoded_content = base64.b64encode(content.encode()).decode()
171
 
@@ -212,11 +206,9 @@ def add_log_line(line):
212
  timestamp = datetime.now().strftime('%H:%M:%S')
213
  log_entry = f"[{timestamp}] {line}"
214
  logs_history.append(log_entry)
215
- # Keep only last 1000 lines
216
  if len(logs_history) > 1000:
217
  logs_history = logs_history[-1000:]
218
  output_queue.put(log_entry)
219
- # Save to file periodically (every 10 lines)
220
  if len(logs_history) % 10 == 0:
221
  save_logs_to_file()
222
 
@@ -227,7 +219,6 @@ def start_minecraft():
227
  print("[MC] Starting Minecraft server...", flush=True)
228
  add_log_line("[SYSTEM] Starting Minecraft server...")
229
 
230
- # Record start time
231
  server_start_time = time.time()
232
  save_uptime_to_file()
233
 
@@ -356,10 +347,27 @@ HTML_TEMPLATE = """
356
  display: flex;
357
  align-items: center;
358
  justify-content: center;
 
359
  }
360
 
361
  .nav-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  /* Main Layout */
364
  .main-container {
365
  display: flex;
@@ -457,6 +465,23 @@ HTML_TEMPLATE = """
457
  .terminal-line.success { color: #3fb950; }
458
  .terminal-line.system { color: #a855f7; font-weight: 500; }
459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  /* Command Input Area */
461
  .command-area {
462
  background: #161b22;
@@ -642,6 +667,90 @@ HTML_TEMPLATE = """
642
 
643
  .sidebar-overlay.active { display: block; }
644
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  /* ============================================
646
  MOBILE RESPONSIVE BREAKPOINTS
647
  ============================================ */
@@ -750,7 +859,7 @@ HTML_TEMPLATE = """
750
  }
751
 
752
  #commandInput {
753
- font-size: 16px; /* Prevents zoom on iOS */
754
  }
755
 
756
  .send-btn {
@@ -776,6 +885,11 @@ HTML_TEMPLATE = """
776
  .stat-value {
777
  font-size: 14px;
778
  }
 
 
 
 
 
779
  }
780
 
781
  /* Landscape mobile */
@@ -826,11 +940,59 @@ HTML_TEMPLATE = """
826
  </a>
827
  </div>
828
  <div class="nav-right">
829
- <button class="nav-btn" title="Settings"><i class="fas fa-cog"></i></button>
830
- <button class="nav-btn" title="Full Screen" onclick="toggleFullscreen()"><i class="fas fa-expand"></i></button>
 
 
 
 
 
831
  </div>
832
  </nav>
833
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  <!-- Sidebar Overlay -->
835
  <div class="sidebar-overlay" id="sidebarOverlay" onclick="toggleSidebar()"></div>
836
 
@@ -952,6 +1114,9 @@ HTML_TEMPLATE = """
952
  const connectionBar = document.getElementById('connectionBar');
953
  const sidebar = document.getElementById('sidebar');
954
  const sidebarOverlay = document.getElementById('sidebarOverlay');
 
 
 
955
 
956
  let ws = null;
957
  let reconnectAttempts = 0;
@@ -959,6 +1124,8 @@ HTML_TEMPLATE = """
959
  let isServerRunning = false;
960
  let sidebarOpen = false;
961
  let serverStartTime = null;
 
 
962
 
963
  function updateConnectionStatus(status, message) {
964
  connectionBar.className = 'connection-bar ' + status;
@@ -968,6 +1135,16 @@ HTML_TEMPLATE = """
968
  'connecting': 'fa-circle-notch fa-spin'
969
  };
970
  connectionBar.innerHTML = '<i class="fas ' + icons[status] + '"></i><span>' + message + '</span>';
 
 
 
 
 
 
 
 
 
 
971
  }
972
 
973
  function updateServerStatus(running, startTime = null) {
@@ -982,9 +1159,11 @@ HTML_TEMPLATE = """
982
  stopBtn.disabled = false;
983
  commandInput.disabled = false;
984
  sendBtn.disabled = false;
 
985
  if (startTime) {
986
- serverStartTime = startTime * 1000; // Convert to milliseconds
987
  }
 
988
  } else {
989
  statusEl.className = 'status-indicator status-offline';
990
  statusEl.innerHTML = '<span class="pulse"></span>Offline';
@@ -993,15 +1172,22 @@ HTML_TEMPLATE = """
993
  stopBtn.disabled = true;
994
  commandInput.disabled = true;
995
  sendBtn.disabled = true;
 
996
  serverStartTime = null;
 
997
  }
998
  }
999
 
1000
  function addTerminalLine(text, type = '') {
 
 
 
 
 
 
1001
  const line = document.createElement('div');
1002
  line.className = 'terminal-line ' + type;
1003
 
1004
- // Check if it's a system message
1005
  if (text.includes('[SYSTEM]')) {
1006
  line.className += ' system';
1007
  } else if (text.includes('> ')) {
@@ -1018,12 +1204,36 @@ HTML_TEMPLATE = """
1018
  terminal.appendChild(line);
1019
  terminal.scrollTop = terminal.scrollHeight;
1020
 
1021
- // Limit terminal lines for performance on mobile
1022
  while (terminal.children.length > 500) {
1023
  terminal.removeChild(terminal.firstChild);
1024
  }
1025
  }
1026
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1027
  function formatUptime(seconds) {
1028
  if (!seconds || seconds <= 0) return 'Offline';
1029
 
@@ -1042,11 +1252,14 @@ HTML_TEMPLATE = """
1042
  if (serverStartTime && isServerRunning) {
1043
  const now = Date.now();
1044
  const uptime = Math.floor((now - serverStartTime) / 1000);
1045
- document.getElementById('uptime').textContent = formatUptime(uptime);
 
1046
  document.getElementById('uptimeSub').textContent = 'Running';
 
1047
  } else {
1048
  document.getElementById('uptime').textContent = 'Offline';
1049
  document.getElementById('uptimeSub').textContent = 'Not running';
 
1050
  }
1051
  }
1052
 
@@ -1069,19 +1282,24 @@ HTML_TEMPLATE = """
1069
  if (data.type === 'output') {
1070
  addTerminalLine(data.data);
1071
  } else if (data.type === 'stats') {
 
1072
  document.getElementById('cpuLoad').textContent = data.cpu.toFixed(1) + '%';
1073
  document.getElementById('memoryUsage').textContent = data.memory.toFixed(1) + '%';
1074
  const memTotal = (data.memoryTotal / 1024 / 1024 / 1024).toFixed(2);
1075
  const memUsed = ((data.memoryTotal * data.memory / 100) / 1024 / 1024 / 1024).toFixed(2);
1076
  document.getElementById('memorySub').textContent = memUsed + ' / ' + memTotal + ' GB';
1077
 
 
 
 
 
1078
  updateServerStatus(data.running, data.serverStartTime);
1079
 
1080
  if (data.players !== undefined) {
1081
  document.getElementById('playerCount').textContent = data.players;
 
1082
  }
1083
  } else if (data.type === 'history') {
1084
- // Load history logs
1085
  data.logs.forEach(log => {
1086
  addTerminalLine(log);
1087
  });
@@ -1108,6 +1326,11 @@ HTML_TEMPLATE = """
1108
  function sendInput() {
1109
  const cmd = commandInput.value.trim();
1110
  if (cmd && ws && ws.readyState === WebSocket.OPEN) {
 
 
 
 
 
1111
  ws.send(JSON.stringify({type: 'command', data: cmd}));
1112
  commandInput.value = '';
1113
  }
@@ -1119,8 +1342,27 @@ HTML_TEMPLATE = """
1119
  }
1120
  }
1121
 
 
 
 
 
1122
  commandInput.addEventListener('keypress', (e) => {
1123
- if (e.key === 'Enter') sendInput();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1124
  });
1125
 
1126
  function switchTab(tab, element) {
@@ -1147,6 +1389,14 @@ HTML_TEMPLATE = """
1147
  }
1148
  }
1149
 
 
 
 
 
 
 
 
 
1150
  // Close sidebar when clicking outside on mobile
1151
  document.addEventListener('click', (e) => {
1152
  if (window.innerWidth <= 768 && sidebarOpen) {
@@ -1214,7 +1464,7 @@ def websocket(ws):
1214
  if logs_history:
1215
  ws.send(json.dumps({
1216
  'type': 'history',
1217
- 'logs': logs_history[-100:] # Send last 100 lines
1218
  }))
1219
  except Exception as e:
1220
  print(f"[WS] History send error: {e}", flush=True)
@@ -1226,7 +1476,6 @@ def websocket(ws):
1226
  memory = psutil.virtual_memory()
1227
  running = mc_process is not None and mc_process.poll() is None
1228
 
1229
- # Calculate actual uptime from server_start_time
1230
  uptime_to_send = None
1231
  if running and server_start_time:
1232
  uptime_to_send = server_start_time
@@ -1302,17 +1551,14 @@ signal.signal(signal.SIGTERM, cleanup)
1302
  signal.signal(signal.SIGINT, cleanup)
1303
 
1304
  if __name__ == '__main__':
1305
- # Load previous logs and uptime
1306
  load_logs_from_file()
1307
  load_uptime_from_file()
1308
 
1309
  print("[MAIN] Starting web server...", flush=True)
1310
 
1311
- # Start Minecraft immediately
1312
  mc_thread = threading.Thread(target=start_minecraft, daemon=True)
1313
  mc_thread.start()
1314
 
1315
- # Start GitHub push thread (every 5 minutes)
1316
  def github_push_loop():
1317
  while True:
1318
  time.sleep(300)
@@ -1339,11 +1585,13 @@ fi
1339
 
1340
  echo ""
1341
  echo "=========================================="
1342
- echo "✅ Minecraft Server with Fixed Uptime + Persistent Logs is LIVE!"
1343
  echo "🌐 Access at: https://your-space.hf.space"
1344
  echo "📱 Mobile optimized interface enabled"
1345
  echo "💾 Logs are now persistent across refreshes"
1346
  echo "⏰ Uptime tracking fixed"
 
 
1347
  echo "=========================================="
1348
 
1349
  # Keep container alive
 
80
  echo ""
81
 
82
  # ==========================================
83
+ # WEB TERMINAL SERVER (Real-time Typing + Status Icon)
84
  # ==========================================
85
  cat > /app/web_terminal.py << 'PYTHON_SCRIPT'
86
  import os
 
120
  if os.path.exists(LOGS_FILE):
121
  with open(LOGS_FILE, 'r', encoding='utf-8') as f:
122
  logs_history = f.read().splitlines()
 
123
  logs_history = logs_history[-1000:]
124
  print(f"[LOGS] Loaded {len(logs_history)} lines from file", flush=True)
125
  except Exception as e:
 
140
  try:
141
  if not GITHUB_TOKEN:
142
  return
 
 
143
  if not os.path.exists(LOGS_FILE):
144
  return
145
 
146
  with open(LOGS_FILE, 'r', encoding='utf-8') as f:
147
  content = f.read()
148
 
 
149
  api_url = f"https://api.github.com/repos/{GITHUB_REPO}/contents/logs/console_logs.txt"
150
  headers = {
151
  "Authorization": f"token {GITHUB_TOKEN}",
152
  "Accept": "application/vnd.github.v3+json"
153
  }
154
 
 
155
  sha = None
156
  try:
157
  resp = requests.get(api_url, headers=headers, timeout=10)
 
160
  except:
161
  pass
162
 
 
163
  import base64
164
  encoded_content = base64.b64encode(content.encode()).decode()
165
 
 
206
  timestamp = datetime.now().strftime('%H:%M:%S')
207
  log_entry = f"[{timestamp}] {line}"
208
  logs_history.append(log_entry)
 
209
  if len(logs_history) > 1000:
210
  logs_history = logs_history[-1000:]
211
  output_queue.put(log_entry)
 
212
  if len(logs_history) % 10 == 0:
213
  save_logs_to_file()
214
 
 
219
  print("[MC] Starting Minecraft server...", flush=True)
220
  add_log_line("[SYSTEM] Starting Minecraft server...")
221
 
 
222
  server_start_time = time.time()
223
  save_uptime_to_file()
224
 
 
347
  display: flex;
348
  align-items: center;
349
  justify-content: center;
350
+ position: relative;
351
  }
352
 
353
  .nav-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
354
 
355
+ /* Status indicator dot on status icon */
356
+ .nav-btn .status-dot {
357
+ position: absolute;
358
+ bottom: 6px;
359
+ right: 6px;
360
+ width: 8px;
361
+ height: 8px;
362
+ border-radius: 50%;
363
+ background: #22c55e;
364
+ border: 2px solid #252a30;
365
+ }
366
+
367
+ .nav-btn .status-dot.offline {
368
+ background: #ef4444;
369
+ }
370
+
371
  /* Main Layout */
372
  .main-container {
373
  display: flex;
 
465
  .terminal-line.success { color: #3fb950; }
466
  .terminal-line.system { color: #a855f7; font-weight: 500; }
467
 
468
+ /* Typing Preview Line */
469
+ .typing-preview {
470
+ color: #6e7681;
471
+ font-style: italic;
472
+ opacity: 0.8;
473
+ }
474
+
475
+ .typing-preview .cursor {
476
+ animation: blink 1s infinite;
477
+ color: #58a6ff;
478
+ }
479
+
480
+ @keyframes blink {
481
+ 0%, 50% { opacity: 1; }
482
+ 51%, 100% { opacity: 0; }
483
+ }
484
+
485
  /* Command Input Area */
486
  .command-area {
487
  background: #161b22;
 
667
 
668
  .sidebar-overlay.active { display: block; }
669
 
670
+ /* Status Modal */
671
+ .status-modal {
672
+ display: none;
673
+ position: fixed;
674
+ top: 50%;
675
+ left: 50%;
676
+ transform: translate(-50%, -50%);
677
+ background: #1f2428;
678
+ border: 1px solid #363b42;
679
+ border-radius: 12px;
680
+ padding: 20px;
681
+ z-index: 300;
682
+ min-width: 300px;
683
+ box-shadow: 0 10px 40px rgba(0,0,0,0.5);
684
+ }
685
+
686
+ .status-modal.active {
687
+ display: block;
688
+ }
689
+
690
+ .status-modal-header {
691
+ display: flex;
692
+ justify-content: space-between;
693
+ align-items: center;
694
+ margin-bottom: 15px;
695
+ padding-bottom: 10px;
696
+ border-bottom: 1px solid #363b42;
697
+ }
698
+
699
+ .status-modal-title {
700
+ font-size: 16px;
701
+ font-weight: 600;
702
+ color: #f0f6fc;
703
+ }
704
+
705
+ .status-modal-close {
706
+ background: transparent;
707
+ border: none;
708
+ color: #9ca3af;
709
+ cursor: pointer;
710
+ font-size: 18px;
711
+ }
712
+
713
+ .status-modal-close:hover {
714
+ color: #fff;
715
+ }
716
+
717
+ .status-item {
718
+ display: flex;
719
+ justify-content: space-between;
720
+ padding: 8px 0;
721
+ border-bottom: 1px solid #2a2f35;
722
+ }
723
+
724
+ .status-item:last-child {
725
+ border-bottom: none;
726
+ }
727
+
728
+ .status-label-modal {
729
+ color: #9ca3af;
730
+ font-size: 13px;
731
+ }
732
+
733
+ .status-value-modal {
734
+ color: #f0f6fc;
735
+ font-weight: 500;
736
+ font-size: 13px;
737
+ }
738
+
739
+ .modal-overlay {
740
+ display: none;
741
+ position: fixed;
742
+ top: 0;
743
+ left: 0;
744
+ right: 0;
745
+ bottom: 0;
746
+ background: rgba(0,0,0,0.7);
747
+ z-index: 250;
748
+ }
749
+
750
+ .modal-overlay.active {
751
+ display: block;
752
+ }
753
+
754
  /* ============================================
755
  MOBILE RESPONSIVE BREAKPOINTS
756
  ============================================ */
 
859
  }
860
 
861
  #commandInput {
862
+ font-size: 16px;
863
  }
864
 
865
  .send-btn {
 
885
  .stat-value {
886
  font-size: 14px;
887
  }
888
+
889
+ .status-modal {
890
+ min-width: 90vw;
891
+ max-width: 350px;
892
+ }
893
  }
894
 
895
  /* Landscape mobile */
 
940
  </a>
941
  </div>
942
  <div class="nav-right">
943
+ <button class="nav-btn" id="statusBtn" title="Server Status" onclick="toggleStatusModal()">
944
+ <i class="fas fa-heartbeat"></i>
945
+ <span class="status-dot offline" id="navStatusDot"></span>
946
+ </button>
947
+ <button class="nav-btn" title="Full Screen" onclick="toggleFullscreen()">
948
+ <i class="fas fa-expand"></i>
949
+ </button>
950
  </div>
951
  </nav>
952
 
953
+ <!-- Status Modal -->
954
+ <div class="modal-overlay" id="modalOverlay" onclick="toggleStatusModal()"></div>
955
+ <div class="status-modal" id="statusModal">
956
+ <div class="status-modal-header">
957
+ <div class="status-modal-title">
958
+ <i class="fas fa-server"></i> Server Status
959
+ </div>
960
+ <button class="status-modal-close" onclick="toggleStatusModal()">
961
+ <i class="fas fa-times"></i>
962
+ </button>
963
+ </div>
964
+ <div class="status-content">
965
+ <div class="status-item">
966
+ <span class="status-label-modal">WebSocket</span>
967
+ <span class="status-value-modal" id="modalWsStatus">Connecting...</span>
968
+ </div>
969
+ <div class="status-item">
970
+ <span class="status-label-modal">Minecraft Server</span>
971
+ <span class="status-value-modal" id="modalMcStatus">Offline</span>
972
+ </div>
973
+ <div class="status-item">
974
+ <span class="status-label-modal">Server Uptime</span>
975
+ <span class="status-value-modal" id="modalUptime">--</span>
976
+ </div>
977
+ <div class="status-item">
978
+ <span class="status-label-modal">CPU Usage</span>
979
+ <span class="status-value-modal" id="modalCpu">--%</span>
980
+ </div>
981
+ <div class="status-item">
982
+ <span class="status-label-modal">Memory Usage</span>
983
+ <span class="status-value-modal" id="modalMemory">--%</span>
984
+ </div>
985
+ <div class="status-item">
986
+ <span class="status-label-modal">Active Players</span>
987
+ <span class="status-value-modal" id="modalPlayers">--</span>
988
+ </div>
989
+ <div class="status-item">
990
+ <span class="status-label-modal">Server Address</span>
991
+ <span class="status-value-modal" id="modalAddress">--</span>
992
+ </div>
993
+ </div>
994
+ </div>
995
+
996
  <!-- Sidebar Overlay -->
997
  <div class="sidebar-overlay" id="sidebarOverlay" onclick="toggleSidebar()"></div>
998
 
 
1114
  const connectionBar = document.getElementById('connectionBar');
1115
  const sidebar = document.getElementById('sidebar');
1116
  const sidebarOverlay = document.getElementById('sidebarOverlay');
1117
+ const statusModal = document.getElementById('statusModal');
1118
+ const modalOverlay = document.getElementById('modalOverlay');
1119
+ const navStatusDot = document.getElementById('navStatusDot');
1120
 
1121
  let ws = null;
1122
  let reconnectAttempts = 0;
 
1124
  let isServerRunning = false;
1125
  let sidebarOpen = false;
1126
  let serverStartTime = null;
1127
+ let typingPreviewLine = null;
1128
+ let currentStats = {};
1129
 
1130
  function updateConnectionStatus(status, message) {
1131
  connectionBar.className = 'connection-bar ' + status;
 
1135
  'connecting': 'fa-circle-notch fa-spin'
1136
  };
1137
  connectionBar.innerHTML = '<i class="fas ' + icons[status] + '"></i><span>' + message + '</span>';
1138
+
1139
+ // Update modal status
1140
+ const wsStatus = document.getElementById('modalWsStatus');
1141
+ if (status === 'connected') {
1142
+ wsStatus.innerHTML = '<span style="color: #22c55e;">● Connected</span>';
1143
+ } else if (status === 'disconnected') {
1144
+ wsStatus.innerHTML = '<span style="color: #ef4444;">● Disconnected</span>';
1145
+ } else {
1146
+ wsStatus.innerHTML = '<span style="color: #eab308;">● Connecting...</span>';
1147
+ }
1148
  }
1149
 
1150
  function updateServerStatus(running, startTime = null) {
 
1159
  stopBtn.disabled = false;
1160
  commandInput.disabled = false;
1161
  sendBtn.disabled = false;
1162
+ navStatusDot.className = 'status-dot';
1163
  if (startTime) {
1164
+ serverStartTime = startTime * 1000;
1165
  }
1166
+ document.getElementById('modalMcStatus').innerHTML = '<span style="color: #22c55e;">● Online</span>';
1167
  } else {
1168
  statusEl.className = 'status-indicator status-offline';
1169
  statusEl.innerHTML = '<span class="pulse"></span>Offline';
 
1172
  stopBtn.disabled = true;
1173
  commandInput.disabled = true;
1174
  sendBtn.disabled = true;
1175
+ navStatusDot.className = 'status-dot offline';
1176
  serverStartTime = null;
1177
+ document.getElementById('modalMcStatus').innerHTML = '<span style="color: #ef4444;">● Offline</span>';
1178
  }
1179
  }
1180
 
1181
  function addTerminalLine(text, type = '') {
1182
+ // Remove typing preview if exists
1183
+ if (typingPreviewLine && typingPreviewLine.parentNode) {
1184
+ typingPreviewLine.parentNode.removeChild(typingPreviewLine);
1185
+ typingPreviewLine = null;
1186
+ }
1187
+
1188
  const line = document.createElement('div');
1189
  line.className = 'terminal-line ' + type;
1190
 
 
1191
  if (text.includes('[SYSTEM]')) {
1192
  line.className += ' system';
1193
  } else if (text.includes('> ')) {
 
1204
  terminal.appendChild(line);
1205
  terminal.scrollTop = terminal.scrollHeight;
1206
 
 
1207
  while (terminal.children.length > 500) {
1208
  terminal.removeChild(terminal.firstChild);
1209
  }
1210
  }
1211
 
1212
+ function updateTypingPreview() {
1213
+ const text = commandInput.value.trim();
1214
+
1215
+ // Remove old preview
1216
+ if (typingPreviewLine && typingPreviewLine.parentNode) {
1217
+ typingPreviewLine.parentNode.removeChild(typingPreviewLine);
1218
+ typingPreviewLine = null;
1219
+ }
1220
+
1221
+ // Show preview if text exists
1222
+ if (text && isServerRunning) {
1223
+ typingPreviewLine = document.createElement('div');
1224
+ typingPreviewLine.className = 'terminal-line typing-preview';
1225
+ typingPreviewLine.innerHTML = '> ' + escapeHtml(text) + '<span class="cursor">_</span>';
1226
+ terminal.appendChild(typingPreviewLine);
1227
+ terminal.scrollTop = terminal.scrollHeight;
1228
+ }
1229
+ }
1230
+
1231
+ function escapeHtml(text) {
1232
+ const div = document.createElement('div');
1233
+ div.textContent = text;
1234
+ return div.innerHTML;
1235
+ }
1236
+
1237
  function formatUptime(seconds) {
1238
  if (!seconds || seconds <= 0) return 'Offline';
1239
 
 
1252
  if (serverStartTime && isServerRunning) {
1253
  const now = Date.now();
1254
  const uptime = Math.floor((now - serverStartTime) / 1000);
1255
+ const formatted = formatUptime(uptime);
1256
+ document.getElementById('uptime').textContent = formatted;
1257
  document.getElementById('uptimeSub').textContent = 'Running';
1258
+ document.getElementById('modalUptime').textContent = formatted;
1259
  } else {
1260
  document.getElementById('uptime').textContent = 'Offline';
1261
  document.getElementById('uptimeSub').textContent = 'Not running';
1262
+ document.getElementById('modalUptime').textContent = 'Offline';
1263
  }
1264
  }
1265
 
 
1282
  if (data.type === 'output') {
1283
  addTerminalLine(data.data);
1284
  } else if (data.type === 'stats') {
1285
+ currentStats = data;
1286
  document.getElementById('cpuLoad').textContent = data.cpu.toFixed(1) + '%';
1287
  document.getElementById('memoryUsage').textContent = data.memory.toFixed(1) + '%';
1288
  const memTotal = (data.memoryTotal / 1024 / 1024 / 1024).toFixed(2);
1289
  const memUsed = ((data.memoryTotal * data.memory / 100) / 1024 / 1024 / 1024).toFixed(2);
1290
  document.getElementById('memorySub').textContent = memUsed + ' / ' + memTotal + ' GB';
1291
 
1292
+ // Update modal stats
1293
+ document.getElementById('modalCpu').textContent = data.cpu.toFixed(1) + '%';
1294
+ document.getElementById('modalMemory').textContent = data.memory.toFixed(1) + '%';
1295
+
1296
  updateServerStatus(data.running, data.serverStartTime);
1297
 
1298
  if (data.players !== undefined) {
1299
  document.getElementById('playerCount').textContent = data.players;
1300
+ document.getElementById('modalPlayers').textContent = data.players;
1301
  }
1302
  } else if (data.type === 'history') {
 
1303
  data.logs.forEach(log => {
1304
  addTerminalLine(log);
1305
  });
 
1326
  function sendInput() {
1327
  const cmd = commandInput.value.trim();
1328
  if (cmd && ws && ws.readyState === WebSocket.OPEN) {
1329
+ // Remove preview immediately on send
1330
+ if (typingPreviewLine && typingPreviewLine.parentNode) {
1331
+ typingPreviewLine.parentNode.removeChild(typingPreviewLine);
1332
+ typingPreviewLine = null;
1333
+ }
1334
  ws.send(JSON.stringify({type: 'command', data: cmd}));
1335
  commandInput.value = '';
1336
  }
 
1342
  }
1343
  }
1344
 
1345
+ // Input event listeners for real-time typing preview
1346
+ commandInput.addEventListener('input', updateTypingPreview);
1347
+ commandInput.addEventListener('keyup', updateTypingPreview);
1348
+
1349
  commandInput.addEventListener('keypress', (e) => {
1350
+ if (e.key === 'Enter') {
1351
+ // Remove preview immediately on enter
1352
+ if (typingPreviewLine && typingPreviewLine.parentNode) {
1353
+ typingPreviewLine.parentNode.removeChild(typingPreviewLine);
1354
+ typingPreviewLine = null;
1355
+ }
1356
+ sendInput();
1357
+ }
1358
+ });
1359
+
1360
+ // Clear preview when input loses focus
1361
+ commandInput.addEventListener('blur', () => {
1362
+ if (typingPreviewLine && typingPreviewLine.parentNode) {
1363
+ typingPreviewLine.parentNode.removeChild(typingPreviewLine);
1364
+ typingPreviewLine = null;
1365
+ }
1366
  });
1367
 
1368
  function switchTab(tab, element) {
 
1389
  }
1390
  }
1391
 
1392
+ function toggleStatusModal() {
1393
+ statusModal.classList.toggle('active');
1394
+ modalOverlay.classList.toggle('active');
1395
+
1396
+ // Update address in modal
1397
+ document.getElementById('modalAddress').textContent = window.location.hostname;
1398
+ }
1399
+
1400
  // Close sidebar when clicking outside on mobile
1401
  document.addEventListener('click', (e) => {
1402
  if (window.innerWidth <= 768 && sidebarOpen) {
 
1464
  if logs_history:
1465
  ws.send(json.dumps({
1466
  'type': 'history',
1467
+ 'logs': logs_history[-100:]
1468
  }))
1469
  except Exception as e:
1470
  print(f"[WS] History send error: {e}", flush=True)
 
1476
  memory = psutil.virtual_memory()
1477
  running = mc_process is not None and mc_process.poll() is None
1478
 
 
1479
  uptime_to_send = None
1480
  if running and server_start_time:
1481
  uptime_to_send = server_start_time
 
1551
  signal.signal(signal.SIGINT, cleanup)
1552
 
1553
  if __name__ == '__main__':
 
1554
  load_logs_from_file()
1555
  load_uptime_from_file()
1556
 
1557
  print("[MAIN] Starting web server...", flush=True)
1558
 
 
1559
  mc_thread = threading.Thread(target=start_minecraft, daemon=True)
1560
  mc_thread.start()
1561
 
 
1562
  def github_push_loop():
1563
  while True:
1564
  time.sleep(300)
 
1585
 
1586
  echo ""
1587
  echo "=========================================="
1588
+ echo "✅ Minecraft Server with Real-time Typing + Status Icon is LIVE!"
1589
  echo "🌐 Access at: https://your-space.hf.space"
1590
  echo "📱 Mobile optimized interface enabled"
1591
  echo "💾 Logs are now persistent across refreshes"
1592
  echo "⏰ Uptime tracking fixed"
1593
+ echo "⌨️ Real-time command typing preview enabled"
1594
+ echo "📊 Status modal with heartbeat icon added"
1595
  echo "=========================================="
1596
 
1597
  # Keep container alive