Move panel to website theme

This commit is contained in:
MCHost
2025-07-06 10:27:26 -04:00
parent 1b58acb38b
commit 82c5970060
6 changed files with 3418 additions and 935 deletions

1492
public/css/main-site.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
@import "tailwindcss";
@font-face {
font-family: 'Minecraft';
src: url('../font/MinecraftRegular-Bmg3.otf') format('opentype');
@ -5,148 +7,69 @@
font-style: normal;
font-display: swap;
}
@media (max-width: 640px) {
.holesail-output-mobile-hidden {
display: none;
}
}
@import "tailwindcss";
@layer base {
/* Sticky footer base styles */
html {
@apply h-full;
margin: 0;
padding: 0;
box-sizing: border-box;
overflow-x: hidden;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
@apply min-h-full flex flex-col;
@apply min-h-full flex flex-col bg-gray-900 text-white;
font-family: 'Roboto', 'Verdana', sans-serif;
overflow-x: hidden;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Minecraft', sans-serif;
@apply font-[Minecraft] text-white;
letter-spacing: 0.5px;
}
#app {
@apply flex-1 flex flex-col;
}
main {
@apply flex-grow;
}
main {
@apply flex-grow container mx-auto px-4 sm:px-6 lg:px-8 py-6;
}
footer {
@apply flex-shrink-0;
@apply bg-gray-800 py-4 text-center;
}
canvas {
@apply w-full max-w-[150px] h-auto;
aspect-ratio: 1/1;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
@apply bg-gray-800;
}
::-webkit-scrollbar-thumb {
@apply bg-blue-600 rounded;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-blue-700;
}
}
@layer components {
.spinner {
@apply border-4 border-gray-700 border-t-white rounded-full w-6 h-6 animate-spin inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#notificationContainer {
@apply fixed bottom-5 right-5 z-[1000] flex flex-col-reverse gap-2;
}
.notification {
@apply bg-gray-800 text-white p-4 rounded-lg flex items-center gap-3 shadow-lg transition-opacity duration-300;
}
.notification.success {
@apply bg-green-700;
}
.notification.error {
@apply bg-red-700;
}
#dockerLogsTerminal {
@apply bg-gray-800 p-4 rounded-lg max-h-48 w-full;
}
.xterm .xterm-viewport {
@apply overflow-y-auto;
scrollbar-width: thin;
scrollbar-color: #4B5563 #1F2937;
}
.xterm .xterm-viewport::-webkit-scrollbar {
@apply w-2;
}
.xterm .xterm-viewport::-webkit-scrollbar-track {
@apply bg-gray-800;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb {
@apply bg-gray-600 rounded;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
@apply bg-gray-500;
}
.xterm .xterm-screen {
@apply w-full;
}
.control-btn {
@apply px-4 py-2 text-sm font-medium transition-all duration-200 min-w-[80px] text-center shadow-sm;
}
.control-btn:hover:not(.disabled-btn) {
@apply -translate-y-px;
}
.control-btn:active:not(.disabled-btn) {
@apply translate-y-0;
}
/* Player button styles */
.tell-player {
@apply bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.give-player {
@apply bg-green-600 hover:bg-green-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.op-player, .deop-player {
@apply bg-purple-600 hover:bg-purple-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.kick-player, .ban-player {
@apply bg-red-600 hover:bg-red-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.teleport-player {
@apply bg-cyan-600 hover:bg-cyan-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.teleport-player:hover:not(.disabled-btn) {
@apply -translate-y-px;
}
.teleport-player:active:not(.disabled-btn) {
@apply translate-y-0;
}
.effect-player {
@apply bg-teal-600 hover:bg-teal-700 px-2 py-1 rounded text-sm font-[Minecraft] transition-all duration-200;
}
.effect-player:hover:not(.disabled-btn) {
@apply -translate-y-px;
}
.effect-player:active:not(.disabled-btn) {
@apply translate-y-0;
.section-bg {
@apply bg-gray-800 p-6 rounded-lg shadow-lg mb-6 w-full max-w-full;
overflow-x: hidden;
}
.modal {
@ -159,6 +82,13 @@
scrollbar-color: #4B5563 #1F2937;
}
@media (max-width: 640px) {
.holesail-output-mobile-hidden {
display: none;
}
}
.modal-content::-webkit-scrollbar {
@apply w-2;
}
@ -179,97 +109,315 @@
@apply absolute top-2 right-2 bg-transparent border-none text-white text-xl cursor-pointer;
}
.disabled-btn {
@apply opacity-50 cursor-not-allowed pointer-events-none;
.control-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded transition duration-200;
}
.disabled-btn:hover {
@apply cursor-not-allowed;
.spinner {
@apply border-4 border-gray-700 border-t-blue-600 rounded-full w-6 h-6 animate-spin inline-block;
}
/* Search box and properties fields */
#searchContainer {
@apply mb-4 block;
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#propertiesSearch {
@apply bg-gray-700 px-4 py-2 rounded text-white w-full block;
#notificationContainer {
@apply fixed bottom-4 right-4 z-[1000] flex flex-col-reverse gap-2;
}
#propertiesList {
@apply space-y-2;
.notification {
@apply bg-gray-800 p-3 rounded-lg text-white flex items-center gap-2 shadow-lg;
max-width: 90%;
font-size: 0.875rem;
}
/* Toggle switch styles */
[role="switch"] {
@apply inline-block relative w-10 h-6 cursor-pointer z-10;
}
[role="switch"] > div:first-child {
@apply w-full h-full rounded-full transition-colors duration-200 ease-in-out;
}
[role="switch"] > div:last-child {
@apply absolute w-4 h-4 rounded-full bg-white top-1 transition-all duration-200 ease-in-out z-[11];
}
[role="switch"][aria-checked="true"] > div:first-child {
.notification.success {
@apply bg-green-600;
}
[role="switch"][aria-checked="false"] > div:first-child {
@apply bg-gray-600;
.notification.error {
@apply bg-red-600;
}
#dockerLogsTerminal {
@apply bg-gray-900 p-4 rounded-lg w-full max-w-full box-border;
height: 250px; /* Increase height to make the section taller */
overflow-y: hidden; /* Remove vertical scrollbar */
overflow-x: hidden; /* Maintain no horizontal scrollbar */
}
.xterm .xterm-viewport {
@apply overflow-y-auto;
height: 250px; /* Increase height to make the section taller */
scrollbar-width: thin;
scrollbar-color: #3b82f6 #1f2937;
}
.xterm .xterm-viewport::-webkit-scrollbar {
width: 8px;
}
.xterm .xterm-viewport::-webkit-scrollbar-track {
@apply bg-gray-800;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb {
@apply bg-blue-600 rounded;
}
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
@apply bg-blue-700;
}
.xterm .xterm-screen {
@apply w-full;
}
.item-entry {
@apply flex items-center gap-2 mb-2 w-full flex-wrap;
}
.item-entry input {
@apply bg-gray-700 p-2 rounded text-white w-full sm:w-auto;
}
.item-entry .item-amount {
@apply w-16 sm:w-12;
}
[role="switch"] {
@apply inline-block relative w-10 h-6 cursor-pointer;
}
[role="switch"] > div:first-child {
@apply block w-full h-full rounded-full bg-gray-700 transition-colors duration-200;
}
[role="switch"] > div:last-child {
@apply absolute w-4 h-4 rounded-full bg-white top-1 transition-all duration-200;
}
[role="switch"][aria-checked="true"] > div:first-child {
@apply bg-blue-600;
}
[role="switch"][aria-checked="true"] > div:last-child {
@apply left-6;
@apply left-5;
}
[role="switch"][aria-checked="false"] > div:last-child {
@apply left-1;
}
#sftpIframe {
@apply w-full rounded;
height: 50vh;
min-height: 300px;
}
#propertiesSearch {
@apply bg-gray-700 p-2 rounded text-white w-full;
}
#propertiesList {
@apply space-y-2;
}
}
@layer utilities {
/* Custom utilities */
.nav-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded transition duration-200;
}
/* Media queries */
@media (max-width: 640px) {
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
.hamburger {
@apply md:hidden flex flex-col justify-center items-center w-10 h-10 fixed top-4 right-4 z-[99999];
}
.hamburger .bar {
@apply w-6 h-0.5 bg-blue-400 mb-1.5 transition-all duration-300;
}
.hamburger.active .bar:nth-child(1) {
@apply translate-y-2 rotate-45;
}
.hamburger.active .bar:nth-child(2) {
@apply opacity-0;
}
.hamburger.active .bar:nth-child(3) {
@apply -translate-y-2 -rotate-45;
}
.mobile-nav-container {
@apply hidden fixed inset-0 flex-col items-center justify-center bg-gray-900 z-[99998] p-4;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.mobile-nav-container.active {
@apply flex opacity-100 visible;
}
.mobile-nav-container ul li a,
.mobile-nav-container ul li button {
@apply block px-4 py-2 rounded text-lg text-white bg-blue-600 hover:bg-blue-700 mb-4;
}
@media (max-width: 768px) {
.section-bg {
@apply p-4;
}
.modal-content {
@apply p-4 max-w-[95%];
}
.section-bg .grid {
@apply grid-cols-1;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg p {
@apply flex flex-col gap-2 break-all;
.section-bg .flex {
@apply flex-col gap-2;
}
.bg-gray-800.p-6.rounded-lg.shadow-lg a,
.bg-gray-800.p-6.rounded-lg.shadow-lg span {
@apply break-words whitespace-normal;
canvas {
@apply max-w-[120px];
}
.bg-gray-800.p-6.rounded-lg.shadow-lg button {
@apply w-full mt-2;
.section-bg input,
.section-bg textarea,
.section-bg select {
@apply p-2 text-sm;
}
.tell-player,
.give-player,
.teleport-player,
.effect-player,
.op-player,
.deop-player,
.kick-player,
.ban-player {
@apply w-full text-center mt-1;
.section-bg pre {
@apply text-sm;
}
#sftpIframe {
@apply h-[40vh] min-h-[300px];
}
#consoleOutput {
@apply h-32 text-sm;
}
.notification {
@apply text-sm p-2 max-w-[80%];
}
h1 { @apply text-3xl; }
h2 { @apply text-xl; }
h3 { @apply text-base; }
p, span, a { @apply text-sm; }
.nav-btn {
@apply hidden;
}
.hamburger {
@apply flex;
}
.control-btn {
@apply px-3 py-1 text-sm;
}
.item-entry {
@apply flex-col gap-1;
}
.item-entry .item-amount {
@apply w-full;
}
}
/* Additional styles */
.bg-gray-800.p-6.rounded-lg.shadow-lg .grid {
@apply overflow-x-hidden;
@media (max-width: 640px) {
.section-bg {
@apply p-3;
}
.modal-content {
@apply p-3 max-w-[95%];
}
canvas {
@apply max-w-[100px];
}
.section-bg input,
.section-bg textarea,
.section-bg select {
@apply p-1 text-xs;
}
.section-bg pre {
@apply text-xs;
}
#sftpIframe {
@apply h-[35vh] min-h-[250px];
}
#consoleOutput {
@apply h-28 text-xs;
}
.notification {
@apply text-xs p-1 max-w-[90%];
}
h1 { @apply text-2xl; }
h2 { @apply text-lg; }
h3 { @apply text-sm; }
p, span, a { @apply text-xs; }
.control-btn {
@apply px-2 py-1 text-xs;
}
}
.bg-gray-800.p-6.rounded-lg.shadow-lg p {
@apply max-w-full;
@media (max-width: 480px) {
.section-bg {
@apply p-2;
}
.modal-content {
@apply p-2 max-w-[98%];
}
canvas {
@apply max-w-[80px];
}
.section-bg input,
.section-bg textarea,
.section-bg select {
@apply p-1 text-xs;
}
.section-bg pre {
@apply text-xs;
}
#sftpIframe {
@apply h-[30vh] min-h-[200px];
}
#consoleOutput {
@apply h-24 text-xs;
}
.notification {
@apply text-xs p-1 max-w-[95%];
}
h1 { @apply text-xl; }
h2 { @apply text-base; }
h3 { @apply text-xs; }
p, span, a { @apply text-xs; }
.control-btn {
@apply px-2 py-1 text-xs;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,137 @@ document.addEventListener('DOMContentLoaded', () => {
'enable-status'
];
const sections = document.querySelectorAll('.section-bg');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
entry.target.style.transform = 'translateY(0)';
entry.target.style.opacity = '1';
entry.target.style.transitionDelay = `${index * 0.1}s`;
}
});
}, { threshold: 0.1 });
// sections.forEach(section => {
// section.style.transform = 'translateY(30px)';
// section.style.opacity = '0';
// section.style.transition = 'transform 0.7s ease-out, opacity 0.7s ease-out';
// observer.observe(section);
// });
// const PARTICLE_POOL_SIZE = 50;
// const particlePool = [];
// const activeParticles = new Set();
// function createParticleElement() {
// const particle = document.createElement('div');
// particle.classList.add('particle');
// if (Math.random() > 0.6) particle.classList.add('large');
// return particle;
// }
// function initializeParticlePool() {
// for (let i = 0; i < PARTICLE_POOL_SIZE; i++) {
// particlePool.push(createParticleElement());
// }
// }
// function resetParticle(particle) {
// particle.style.left = `${Math.random() * 100}%`;
// particle.style.top = `${Math.random() * 100}%`;
// particle.style.animationDelay = `${Math.random() * 8}s`;
// particle.style.animationDuration = `${8 + Math.random() * 6}s`;
// particle.classList.remove('fade-out');
// return particle;
// }
// function spawnParticle() {
// if (particlePool.length === 0 || activeParticles.size >= PARTICLE_POOL_SIZE) return;
// const particle = resetParticle(particlePool.pop());
// if (!particle.parentNode) document.body.appendChild(particle);
// activeParticles.add(particle);
// setTimeout(() => {
// particle.classList.add('fade-out');
// setTimeout(() => {
// activeParticles.delete(particle);
// particlePool.push(particle);
// }, 500);
// }, 14000);
// }
// function animate() {
// if (Math.random() < 0.1) spawnParticle(); // Reduced spawn frequency
// requestAnimationFrame(animate);
// }
// // Initialize and start
// setTimeout(() => {
// initializeParticlePool();
// animate();
// }, 500);
function throttle(fn, wait) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= wait) {
fn.apply(this, args);
lastTime = now;
}
};
}
// const buttons = document.querySelectorAll('.btn-minecraft');
// buttons.forEach(button => {
// button.addEventListener('mousemove', (e) => {
// const rect = button.getBoundingClientRect();
// const x = e.clientX - rect.left;
// const y = e.clientY - rect.top;
// button.style.setProperty('--x', `${x}px`);
// button.style.setProperty('--y', `${y}px`);
// });
// });
// Hamburger Menu Toggle
const hamburger = document.querySelector('.hamburger');
const mobileNav = document.querySelector('[data-mobile-nav]');
const navLinks = mobileNav.querySelectorAll('a');
// Debounce function to prevent rapid clicks
function debounce(fn, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), wait);
};
}
hamburger.addEventListener('click', debounce(() => {
console.log('Hamburger clicked, toggling menu');
mobileNav.classList.toggle('active');
hamburger.classList.toggle('active');
}, 100));
navLinks.forEach(link => {
link.addEventListener('click', () => {
console.log('Nav link clicked, closing menu');
mobileNav.classList.remove('active');
hamburger.classList.remove('active');
});
});
document.addEventListener('click', (e) => {
if (!mobileNav.contains(e.target) && !hamburger.contains(e.target) && mobileNav.classList.contains('active')) {
console.log('Clicked outside, closing menu');
mobileNav.classList.remove('active');
hamburger.classList.remove('active');
}
});
const elements = {
loginPage: document.getElementById('loginPage'),
loginApiKey: document.getElementById('loginApiKey'),
@ -321,7 +452,7 @@ document.addEventListener('DOMContentLoaded', () => {
terminal.clear();
} else {
terminal = new Terminal({
rows: 8,
rows: 22,
fontSize: 14,
fontFamily: 'monospace',
theme: {
@ -1038,9 +1169,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
elements.loginPage.classList.remove('hidden');
elements.mainContent.classList.add('hidden');
elements.authControls.innerHTML = '<input id="apiKey" type="text" placeholder="Enter API Key" class="bg-gray-700 px-4 py-2 rounded text-white">';
elements.apiKeyInput = document.getElementById('apiKey');
elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
if (ws) {
ws.close();
ws = null;
@ -1069,13 +1198,35 @@ document.addEventListener('DOMContentLoaded', () => {
}
elements.loginPage.classList.add('hidden');
elements.mainContent.classList.remove('hidden');
elements.authControls.innerHTML = '<button id="logoutBtn" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded">Logout</button>';
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', handleLogout);
} else {
console.error('Logout button not found after insertion');
}
// Verify buttons exist
const logoutBtn = document.getElementById('logoutBtn');
const mobileLogoutBtn = document.getElementById('mobileLogoutBtn');
if (logoutBtn) {
console.log('Desktop logout button found');
} else {
console.error('Desktop logout button (#logoutBtn) not found');
}
if (mobileLogoutBtn) {
console.log('Mobile logout button found');
} else {
console.error('Mobile logout button (#mobileLogoutBtn) not found');
}
// Remove any existing logout listeners to prevent duplicates
document.removeEventListener('click', handleLogoutClick);
// Event delegation for logout buttons
function handleLogoutClick(event) {
if (event.target.id === 'logoutBtn' || event.target.id === 'mobileLogoutBtn') {
console.log(`Logout button clicked: ${event.target.id}`);
handleLogout();
}
}
document.addEventListener('click', handleLogoutClick);
initializeCharts();
initializeTerminal();
}
@ -1095,6 +1246,18 @@ document.addEventListener('DOMContentLoaded', () => {
showLoginPage();
}
async function handleRefresh() {
if (ws && ws.readyState === WebSocket.OPEN) {
const key = `action-refresh`;
const notification = showNotification('Refreshing server data...', 'loading', key);
ws.send(JSON.stringify({ type: 'refresh' }));
initializeTerminal();
setTimeout(() => updateNotification(notification, 'Server data refreshed successfully', 'success', key), 1000);
} else {
showNotification('Not connected to server. Please log in.', 'error', 'ws-disconnected');
}
}
function updatePagination() {
const totalPages = Math.max(1, Math.ceil(totalResults / resultsPerPage));
elements.pagination.innerHTML = '';
@ -1687,7 +1850,6 @@ document.addEventListener('DOMContentLoaded', () => {
switchContainer.className = 'relative inline-block';
switchContainer.setAttribute('role', 'switch');
switchContainer.setAttribute('aria-checked', value.toLowerCase());
switchContainer.setAttribute('tabindex', '0');
switchContainer.dataset.name = key;
switchContainer.style.width = '40px';
switchContainer.style.height = '24px';
@ -1918,7 +2080,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
// elements.apiKeyInput.addEventListener('change', handleApiKeyChange);
elements.generateMyLinkBtn.addEventListener('click', async () => {
try {
@ -2065,6 +2227,8 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
elements.updateModsBtn.addEventListener('click', updateMods);
elements.closeUpdateModsBtn.addEventListener('click', () => {
@ -2177,4 +2341,42 @@ document.addEventListener('DOMContentLoaded', () => {
showLoginPage();
}
window.showNotification = showNotification;
// Add this at the end of the document.addEventListener('DOMContentLoaded', ...) block
function applyResponsiveStyles() {
const sections = document.querySelectorAll('.section-bg');
sections.forEach(section => {
section.style.padding = window.innerWidth <= 640 ? '1rem' : window.innerWidth <= 768 ? '1.5rem' : '2rem';
section.style.maxWidth = '100%';
section.style.overflowX = 'hidden';
});
const iframes = document.querySelectorAll('#sftpIframe');
iframes.forEach(iframe => {
iframe.style.height = window.innerWidth <= 640 ? '300px' : window.innerWidth <= 768 ? '400px' : '650px';
iframe.style.minHeight = iframe.style.height;
iframe.style.width = '100%';
iframe.style.maxWidth = '100%';
});
const canvases = document.querySelectorAll('#memoryMeter, #cpuMeter');
canvases.forEach(canvas => {
canvas.style.width = window.innerWidth <= 640 ? '80px' : window.innerWidth <= 768 ? '100px' : '150px';
canvas.style.height = canvas.style.width;
});
const terminals = document.querySelectorAll('#dockerLogsTerminal');
terminals.forEach(terminal => {
terminal.style.maxHeight = window.innerWidth <= 640 ? '7rem' : window.innerWidth <= 768 ? '8rem' : '12rem';
});
const consoles = document.querySelectorAll('#consoleOutput');
consoles.forEach(console => {
console.style.height = window.innerWidth <= 640 ? '7rem' : window.innerWidth <= 768 ? '8rem' : '12rem';
});
}
window.addEventListener('resize', applyResponsiveStyles);
});

View File

@ -4,6 +4,7 @@ module.exports = {
'./public/**/*.html',
'./public/**/*.js',
'./public/*.html',
'./public/css/*.css',
'./public/*.js'
],
theme: {