<?php
// Force login + disable caching
session_start();
require_once __DIR__ . '/../core/db_config.php';
// 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:#0b0f14; color:#e6edf3;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Inter, "Helvetica Neue", Arial;
}
.body-lock { overflow: hidden !important; touch-action: none; }
/* Topbar */
.topbar {
position: sticky; top: 0; z-index: 5;
background: linear-gradient(180deg, rgba(11,15,20,.95), rgba(11,15,20,.85));
border-bottom: 1px solid #1e2633; backdrop-filter: blur(6px);
}
.topbar-inner {
max-width:1000px; margin:0 auto; padding:.75rem;
display:flex; align-items:center; gap:.75rem;
/* no overflow here; let #buttonRow handle scrolling */
}
/* Chips row: takes remaining space and scrolls horizontally */
#buttonRow {
flex: 1 1 auto; min-width: 0; /* allow shrinking */
display: flex; gap: .75rem; align-items: center;
overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
-webkit-overflow-scrolling: touch;
}
/* 3-dots container: pinned at far right */
#menuContainer {
flex: 0 0 auto; margin-left: .25rem; position: relative;
}
.chip {
flex:0 0 auto; border:1px solid #2a3648; background:#1a2332; color:#e6edf3;
padding:.55rem .9rem; border-radius:999px; font-weight:600; cursor:pointer;
transition: background .15s ease;
}
.chip:hover { background:#263244; }
/* Content */
.container { max-width:1000px; margin:1.25rem auto; padding:0 .75rem; }
.lead { color:#9aa4b2; }
/* 3-dots dropdown */
.menu-trigger { width:38px; text-align:center; }
.menu-list {
display:none; position:absolute; right:0; top:calc(100% + 6px);
background:#1a2332; border:1px solid #2a3648; border-radius:10px;
min-width:180px; padding:.25rem 0; z-index:9999; box-shadow: 0 10px 30px rgba(0,0,0,.3);
}
.menu-list.open { display:block; }
.menu-item {
display:block; width:100%; text-align:left; background:none; border:none; color:#e6edf3;
padding:.6rem 1rem; cursor:pointer; font: inherit;
}
.menu-item:hover { background:#263244; }
</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">
<h1>Modular Overlay Index</h1>
<p class="lead">Each module adds its own button and overlay content. If modules register extra actions, a 3-dots menu appears on the right.</p>
</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('code.js') ?>" defer></script>
<script src="<?= asset('menu.js') ?>" defer></script>
<script src="<?= asset('module.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.onclick = () => 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.textContent = '⋮';
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 { m.action && m.action(); } catch(e) { console.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);
}
function closeMenu(){
dropdown.classList.remove('open');
dropdown.setAttribute('aria-hidden','true');
trigger.setAttribute('aria-expanded','false');
document.removeEventListener('click', handleOutside);
document.removeEventListener('keydown', escClose);
}
function handleOutside(e){ if (!menuContainer.contains(e.target)) closeMenu(); }
function escClose(e){ if (e.key === 'Escape') closeMenu(); }
trigger.addEventListener('click', (e)=>{
e.stopPropagation();
dropdown.classList.contains('open') ? closeMenu() : openMenu();
});
}
});
</script>
</body>
</html>