// ============================================ // Minecraft Server Panel - Main JavaScript // ============================================ // Toast Notification System function showToast(message, type = 'info') { const container = document.getElementById('toast-container'); if (!container) return; const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; container.appendChild(toast); // Auto remove after 5 seconds setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease forwards'; setTimeout(() => toast.remove(), 300); }, 5000); } // Add slideOut animation const style = document.createElement('style'); style.textContent = ` @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(style); // API Helper Functions async function apiCall(endpoint, method = 'GET', data = null) { const options = { method, headers: { 'Content-Type': 'application/json' } }; if (data && method !== 'GET') { options.body = JSON.stringify(data); } try { const response = await fetch(endpoint, options); const result = await response.json(); return result; } catch (error) { console.error('API Error:', error); throw error; } } // Server Control Functions async function serverControl(action) { try { const result = await apiCall(`/api/server/${action}`, 'POST'); showToast(result.message, result.success ? 'success' : 'error'); return result; } catch (error) { showToast(`Failed to ${action} server`, 'error'); } } // Utility Functions function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function formatDuration(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); if (hours > 0) { return `${hours}h ${minutes}m ${secs}s`; } else if (minutes > 0) { return `${minutes}m ${secs}s`; } return `${secs}s`; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Debounce function for search inputs function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Update server status indicator in sidebar function updateSidebarStatus(running, starting = false) { const indicator = document.getElementById('server-status-indicator'); if (!indicator) return; const dot = indicator.querySelector('.status-dot'); const text = indicator.querySelector('.status-text'); if (running) { dot.className = 'status-dot online'; text.textContent = 'Online'; } else if (starting) { dot.className = 'status-dot starting'; text.textContent = 'Starting'; } else { dot.className = 'status-dot offline'; text.textContent = 'Offline'; } } // Global keyboard shortcuts document.addEventListener('keydown', (e) => { // Ctrl/Cmd + K: Focus search if exists if ((e.ctrlKey || e.metaKey) && e.key === 'k') { const searchInput = document.querySelector('input[type="text"][placeholder*="Search"]'); if (searchInput) { e.preventDefault(); searchInput.focus(); } } // Escape: Close modals if (e.key === 'Escape') { const activeModal = document.querySelector('.modal.active'); if (activeModal) { activeModal.classList.remove('active'); } } }); // Initialize tooltips (if any elements have data-tooltip) document.querySelectorAll('[data-tooltip]').forEach(el => { el.addEventListener('mouseenter', (e) => { const tooltip = document.createElement('div'); tooltip.className = 'tooltip'; tooltip.textContent = el.dataset.tooltip; document.body.appendChild(tooltip); const rect = el.getBoundingClientRect(); tooltip.style.top = `${rect.bottom + 8}px`; tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2)}px`; }); el.addEventListener('mouseleave', () => { document.querySelectorAll('.tooltip').forEach(t => t.remove()); }); }); // Auto-refresh status periodically if (typeof io !== 'undefined') { // Socket.IO is available, status updates will come via websocket console.log('WebSocket mode enabled'); } else { // Fallback to polling setInterval(async () => { try { const status = await apiCall('/api/status'); updateSidebarStatus(status.running, status.starting); } catch (e) { // Ignore errors during polling } }, 10000); } // Confirm before leaving page if there are unsaved changes let hasUnsavedChanges = false; window.addEventListener('beforeunload', (e) => { if (hasUnsavedChanges) { e.preventDefault(); e.returnValue = ''; } }); // Mark page as having unsaved changes when editing document.querySelectorAll('textarea, input[type="text"]').forEach(el => { el.addEventListener('input', () => { if (el.classList.contains('code-editor') || el.closest('.settings-grid')) { hasUnsavedChanges = true; } }); }); // Reset unsaved changes flag after successful save const originalShowToast = showToast; showToast = function(message, type) { if (type === 'success' && (message.includes('saved') || message.includes('updated'))) { hasUnsavedChanges = false; } originalShowToast(message, type); }; // File drag and drop prevention (except on upload zones) document.addEventListener('dragover', (e) => { if (!e.target.closest('.upload-zone')) { e.preventDefault(); } }); document.addEventListener('drop', (e) => { if (!e.target.closest('.upload-zone')) { e.preventDefault(); } }); // Console log for debugging console.log('MC Server Panel loaded'); console.log('Version: 1.0.0'); console.log('Minecraft: 1.21.1'); console.log('Fabric: 0.17.1');