🐘
index.php
Back
📝 Php ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<?php // Force login + disable caching session_start(); require_once __DIR__ . '/../core/db_config.php'; // Set page title for header $GLOBALS['page_title'] = 'App Dashboard'; // Anti-cache headers (dev) header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); header('Expires: 0'); // Redirect if not logged in if (empty($_SESSION['username'])) { $redirect = urlencode($_SERVER['REQUEST_URI'] ?? '/'); header("Location: /core/auth/login.php?redirect={$redirect}"); exit; } // Cache-busting helper (absolute & relative paths) function asset($path) { $isAbsolute = strlen($path) && $path[0] === '/'; $abs = $isAbsolute ? rtrim($_SERVER['DOCUMENT_ROOT'], '/') . $path : __DIR__ . '/' . $path; $v = is_file($abs) ? filemtime($abs) : time(); return $path . '?v=' . $v; } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>App Index</title> <!-- Shared overlay CSS --> <link rel="stylesheet" href="<?= asset('/core/css/overlay.css') ?>"> <style> /* Global */ html, body { height: 100%; margin: 0; overscroll-behavior-y: contain; } body { background: linear-gradient(135deg, #0a0f1c 0%, #1e293b 100%); color: #f1f5f9; font-family: system-ui, -apple-system, Segoe UI, Roboto, Inter, "Helvetica Neue", Arial; line-height: 1.6; } .body-lock { overflow: hidden !important; touch-action: none; } /* Topbar */ .topbar { position: sticky; top: 0; z-index: 50; background: linear-gradient(135deg, rgba(10, 15, 28, 0.95) 0%, rgba(30, 41, 59, 0.95) 100%); border-bottom: 1px solid rgba(71, 85, 105, 0.3); backdrop-filter: blur(20px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } .topbar-inner { max-width: 1200px; margin: 0 auto; padding: 1rem; display: flex; align-items: center; gap: 1rem; } /* Chips row: takes remaining space and scrolls horizontally */ #buttonRow { flex: 1 1 auto; min-width: 0; display: flex; gap: 0.875rem; align-items: center; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; scrollbar-color: rgba(148, 163, 184, 0.5) transparent; -webkit-overflow-scrolling: touch; padding: 0.25rem 0; } #buttonRow::-webkit-scrollbar { height: 4px; } #buttonRow::-webkit-scrollbar-track { background: transparent; } #buttonRow::-webkit-scrollbar-thumb { background: rgba(148, 163, 184, 0.5); border-radius: 2px; } /* 3-dots container: pinned at far right */ #menuContainer { flex: 0 0 auto; margin-left: 0.5rem; position: relative; } .chip { flex: 0 0 auto; border: 1px solid rgba(71, 85, 105, 0.4); background: linear-gradient(135deg, rgba(30, 41, 59, 0.8) 0%, rgba(51, 65, 85, 0.8) 100%); color: #f1f5f9; padding: 0.625rem 1.125rem; border-radius: 0.75rem; font-weight: 600; cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); backdrop-filter: blur(10px); box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); white-space: nowrap; font-size: 0.9rem; } .chip:hover { background: linear-gradient(135deg, rgba(51, 65, 85, 0.9) 0%, rgba(71, 85, 105, 0.9) 100%); transform: translateY(-1px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } .chip:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4); transform: translateY(-1px); } .chip:active { transform: translateY(0); box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } /* Content */ .container { max-width: 1200px; margin: 2rem auto; padding: 0 1rem; } .hero { text-align: center; padding: 3rem 0; background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(147, 51, 234, 0.1) 100%); border-radius: 1rem; border: 1px solid rgba(71, 85, 105, 0.3); backdrop-filter: blur(10px); margin-bottom: 2rem; } .hero h1 { font-size: 2.5rem; font-weight: 800; margin: 0 0 1rem 0; background: linear-gradient(135deg, #60a5fa, #a78bfa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; letter-spacing: -0.025em; } .lead { color: #94a3b8; font-size: 1.125rem; max-width: 600px; margin: 0 auto; line-height: 1.7; } /* 3-dots dropdown */ .menu-trigger { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; border-radius: 0.75rem; } .menu-list { display: none; position: absolute; right: 0; top: calc(100% + 8px); background: linear-gradient(135deg, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.95) 100%); border: 1px solid rgba(71, 85, 105, 0.4); border-radius: 0.75rem; min-width: 200px; padding: 0.5rem; z-index: 9999; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); backdrop-filter: blur(20px); animation: menu-fade-in 0.2s ease-out; } .menu-list.open { display: block; } @keyframes menu-fade-in { from { opacity: 0; transform: translateY(-8px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } .menu-item { display: block; width: 100%; text-align: left; background: none; border: none; color: #f1f5f9; padding: 0.75rem 1rem; cursor: pointer; font: inherit; border-radius: 0.5rem; transition: all 0.15s ease; font-weight: 500; } .menu-item:hover { background: rgba(51, 65, 85, 0.6); transform: translateX(2px); } /* Mobile responsive */ @media (max-width: 768px) { .topbar-inner { padding: 0.875rem; } .hero { padding: 2rem 1rem; margin: 1rem 0; } .hero h1 { font-size: 2rem; } .container { margin: 1rem auto; padding: 0 0.875rem; } } @media (max-width: 640px) { .topbar-inner { gap: 0.75rem; } #buttonRow { gap: 0.625rem; } .chip { padding: 0.5rem 0.875rem; font-size: 0.85rem; } .hero h1 { font-size: 1.75rem; } .lead { font-size: 1rem; } .menu-trigger { width: 40px; height: 40px; font-size: 1.125rem; } } @media (max-width: 480px) { .topbar-inner { padding: 0.75rem; } .chip { padding: 0.425rem 0.75rem; font-size: 0.8rem; } .hero { padding: 1.5rem 0.75rem; } .hero h1 { font-size: 1.5rem; } .menu-list { min-width: 180px; } .menu-item { padding: 0.625rem 0.875rem; font-size: 0.9rem; } } </style> </head> <body> <?php include __DIR__ . '/../core/auth/header.php'; ?> <header class="topbar" aria-label="Top navigation"> <div class="topbar-inner"> <div id="buttonRow" role="tablist" aria-label="App sections"></div> <div id="menuContainer" aria-label="More actions"></div> </div> </header> <main class="container"> <div class="hero"> <h1>Modular Overlay System</h1> <p class="lead">Each module adds its own button and overlay content. Experience seamless navigation with our modern, responsive interface.</p> </div> </main> <!-- 1) Empty globals for modules --> <script> window.AppItems = []; // overlay sections window.AppMenu = []; // 3-dots actions (optional) </script> <!-- 2) Shared overlay --> <script src="<?= asset('/core/js/overlay.js') ?>" defer></script> <!-- 3) Modules --> <script src="<?= asset('files.js') ?>" defer></script> <script src="<?= asset('menu.js') ?>" defer></script> <script src="<?= asset('cutout.js') ?>" defer></script> <script src="<?= asset('gameObject.js') ?>" defer></script> <!-- 4) Render chips + menu --> <script defer> document.addEventListener('DOMContentLoaded', () => { // Buttons from AppItems const row = document.getElementById('buttonRow'); row.innerHTML = ''; (window.AppItems || []).forEach((item, i) => { const btn = document.createElement('button'); btn.className = 'chip'; btn.textContent = item.title || `Item ${i+1}`; btn.setAttribute('role', 'tab'); btn.setAttribute('aria-selected', 'false'); btn.onclick = () => { if (window.AppOverlay) { AppOverlay.open(window.AppItems, i, btn); } }; row.appendChild(btn); }); // 3-dots menu const menuContainer = document.getElementById('menuContainer'); menuContainer.innerHTML = ''; const menuItems = window.AppMenu || []; if (menuItems.length > 0) { const trigger = document.createElement('button'); trigger.className = 'chip menu-trigger'; trigger.type = 'button'; trigger.setAttribute('aria-haspopup','true'); trigger.setAttribute('aria-expanded','false'); trigger.setAttribute('aria-label','More actions'); trigger.innerHTML = '⋮'; menuContainer.appendChild(trigger); const dropdown = document.createElement('div'); dropdown.className = 'menu-list'; dropdown.setAttribute('role','menu'); dropdown.setAttribute('aria-hidden','true'); menuContainer.appendChild(dropdown); menuItems.forEach((m, idx) => { const item = document.createElement('button'); item.className = 'menu-item'; item.type = 'button'; item.setAttribute('role','menuitem'); item.textContent = m.label || `Action ${idx+1}`; item.onclick = () => { closeMenu(); try { if (m.action) m.action(); } catch(e) { console.error('Menu action error:', e); } }; dropdown.appendChild(item); }); function openMenu(){ dropdown.classList.add('open'); dropdown.setAttribute('aria-hidden','false'); trigger.setAttribute('aria-expanded','true'); document.addEventListener('click', handleOutside); document.addEventListener('keydown', escClose); // Focus first menu item for keyboard navigation const firstItem = dropdown.querySelector('.menu-item'); if (firstItem) firstItem.focus(); } function closeMenu(){ dropdown.classList.remove('open'); dropdown.setAttribute('aria-hidden','true'); trigger.setAttribute('aria-expanded','false'); document.removeEventListener('click', handleOutside); document.removeEventListener('keydown', escClose); trigger.focus(); // Return focus to trigger } function handleOutside(e){ if (!menuContainer.contains(e.target)) closeMenu(); } function escClose(e){ if (e.key === 'Escape') { e.preventDefault(); closeMenu(); } } // Keyboard navigation in menu dropdown.addEventListener('keydown', (e) => { const items = dropdown.querySelectorAll('.menu-item'); const currentIndex = Array.from(items).indexOf(document.activeElement); switch(e.key) { case 'ArrowDown': e.preventDefault(); const nextIndex = (currentIndex + 1) % items.length; items[nextIndex].focus(); break; case 'ArrowUp': e.preventDefault(); const prevIndex = (currentIndex - 1 + items.length) % items.length; items[prevIndex].focus(); break; case 'Home': e.preventDefault(); items[0].focus(); break; case 'End': e.preventDefault(); items[items.length - 1].focus(); break; } }); trigger.addEventListener('click', (e) => { e.stopPropagation(); dropdown.classList.contains('open') ? closeMenu() : openMenu(); }); } }); </script> </body> </html>