SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,338
Folders
408
Scanned Size
3.84 GB
PHP Files
890
Editable Text Files
12,599
File viewer
guarded to /htdocs
/_inc/app.php
<?php declare(strict_types=1); /** * /_inc/app.php — GaylordSinclair bootstrap (REV: SIMON + GTAG + Log Intelligence + Favicons) * * Provides: * - h() HTML escape helper * - SIMON log intelligence helpers (NDJSON logs in /_logs) * - Global $SITE + $SOCIAL + $NAV * - page_header($title) / page_footer() * * Optional files: * - /_inc/_ga.php => returns GA Measurement ID string (e.g. "G-XXXXXXX") * - /_inc/_key_worlds.php => returns password string (optional) */ if (!defined('GS_APP_LOADED')) define('GS_APP_LOADED', true); /* ========================================================= Helpers ========================================================= */ function h(string $s): string { return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); } /* ========================================================= Paths / logging (SIMON Log Intelligence) ========================================================= */ function gs_docroot(): string { return rtrim((string)($_SERVER['DOCUMENT_ROOT'] ?? ''), '/'); } function gs_log_dir(): string { return gs_docroot() . '/_logs'; } function gs_ensure_log_dir(): void { $dir = gs_log_dir(); if (!is_dir($dir)) { @mkdir($dir, 0755, true); } } /** Stable request id (also used on error pages) */ function gs_request_id(): string { static $rid = null; if ($rid !== null) return $rid; $hdr = (string)($_SERVER['HTTP_X_REQUEST_ID'] ?? ''); if ($hdr !== '') { $rid = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $hdr) ?: bin2hex(random_bytes(8)); return $rid; } $rid = bin2hex(random_bytes(8)) . bin2hex(random_bytes(8)); return $rid; } /** * Append a line of NDJSON to /_logs/<name>.ndjson * Example: gs_log_ndjson('traffic', ['evt'=>'pageview', ...]) */ function gs_log_ndjson(string $name, array $data): void { gs_ensure_log_dir(); $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name) ?: 'site'; $file = gs_log_dir() . '/' . $name . '.ndjson'; $row = $data + [ 'ts' => gmdate('c'), 'rid' => gs_request_id(), 'ip' => (string)($_SERVER['REMOTE_ADDR'] ?? ''), 'ua' => (string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 'host' => (string)($_SERVER['HTTP_HOST'] ?? ''), ]; $json = json_encode($row, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if (!$json) return; @file_put_contents($file, $json . "\n", FILE_APPEND | LOCK_EX); @chmod($file, 0644); } /** Convenience: log a page view */ function gs_log_pageview(string $page_type = 'page', array $extra = []): void { $url = (string)($_SERVER['REQUEST_URI'] ?? ''); $ref = (string)($_SERVER['HTTP_REFERER'] ?? ''); gs_log_ndjson('traffic', $extra + [ 'evt' => 'pageview', 'type' => $page_type, 'url' => $url, 'ref' => $ref, ]); } /** Convenience: log a navigation click */ function gs_log_nav_click(string $label, string $href, array $extra = []): void { gs_log_ndjson('traffic', $extra + [ 'evt' => 'nav_click', 'label' => $label, 'href' => $href, ]); } /* ========================================================= Brand + site settings ========================================================= */ $SITE = $SITE ?? [ 'brand' => [ 'name' => 'GAYLORD SINCLAIR', 'tagline' => 'Books • Art • Comics • Systems', 'desc' => 'A living archive of books, art, comics, and systems—built to preserve, publish, and evolve ideas.', ], 'base_url' => '', // leave blank for relative; or set https://www.gaylordsinclair.com // ---- Favicon / PWA assets ---- // Put your icons in /assets/icons/ (recommended) 'icons' => [ 'favicon_ico' => '/assets/icons/favicon.ico', 'favicon_16' => '/assets/icons/favicon-16x16.png', 'favicon_32' => '/assets/icons/favicon-32x32.png', 'apple_180' => '/assets/icons/apple-touch-icon.png', 'manifest' => '/assets/icons/manifest.json', 'browserconfig'=> '/assets/icons/browserconfig.xml', 'tile_color' => '#070b16', ], ]; $SOCIAL = $SOCIAL ?? [ // 'Instagram' => 'https://instagram.com/...', // 'Facebook' => 'https://facebook.com/...', // 'YouTube' => 'https://youtube.com/...', ]; /* ========================================================= Navigation (Dropdown) ========================================================= */ $NAV = $NAV ?? [ 'Primary' => [ ['Books', '/books/', 'All books, releases, downloads'], ['Art', '/art/', 'Gallery, featured artists (Tilo, etc.)'], ['Comics', '/comics/', 'Simon’s World of Comics'], ['Pressbox', '/pressbox/', 'Press, updates, articles, media kit'], ['Infinity', '/infinity/', 'Digital memorial + legacy sanctuary'], ['Downloads', '/index.php','Files, PDFs, packs, media'], ], 'SIMON Systems' => [ ['SIMON AI', '/simon_ai/', 'Assistant, tools, orb systems'], ['SIMON Civilization','/simonciv/', 'Blueprints, modules, simulation'], ['SIMON Web360', '/web360/', 'Live builder / web IDE'], ['Log Intelligence', '/console.php', 'Server logs + intelligence dashboard'], ], 'Stores + Community' => [ ['Store', 'https://simon.gaylordsinclair.com', 'Marketplace / shop'], ['The Collective', '/collective/', 'Community hub (if applicable)'], ['Contact', '/contact.php', 'Get in touch'], ], 'Locked' => [ ['SIMON Worlds (Locked)', '/simon_worlds/', 'Password-protected worlds / admin'], ], ]; /* ========================================================= Google tag config (recommended: keep ID in _inc/_ga.php) ========================================================= */ $GA_ID = ''; $gaFile = __DIR__ . '/_ga.php'; if (is_file($gaFile)) { $tmp = require $gaFile; if (is_string($tmp)) $GA_ID = trim($tmp); } if ($GA_ID === '') { $GA_ID = 'G-S6MEHB4BEY'; // fallback (ok) } /* ========================================================= Worlds password (optional) ========================================================= */ $WORLDS_PASSWORD = ''; $worldKeyFile = __DIR__ . '/_key_worlds.php'; if (is_file($worldKeyFile)) { $tmp = require $worldKeyFile; if (is_string($tmp)) $WORLDS_PASSWORD = trim($tmp); } /* ========================================================= Layout ========================================================= */ function page_header(string $title = ''): void { global $SITE, $SOCIAL, $NAV, $GA_ID; $fullTitle = $title ? ($title . ' — ' . $SITE['brand']['name']) : $SITE['brand']['name']; $rid = gs_request_id(); // log normal page loads (not errors) gs_log_pageview('page', ['title' => $fullTitle]); $icons = $SITE['icons'] ?? []; $faviconIco = (string)($icons['favicon_ico'] ?? '/favicon.ico'); $favicon16 = (string)($icons['favicon_16'] ?? ''); $favicon32 = (string)($icons['favicon_32'] ?? ''); $apple180 = (string)($icons['apple_180'] ?? ''); $manifest = (string)($icons['manifest'] ?? ''); $browserconf = (string)($icons['browserconfig'] ?? ''); $tileColor = (string)($icons['tile_color'] ?? '#070b16'); ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" /> <title><?= h($fullTitle) ?></title> <meta name="theme-color" content="#070b16" /> <meta name="description" content="<?= h((string)($SITE['brand']['desc'] ?? '')) ?>" /> <meta name="x-simon" content="powered-by-simon" /> <meta name="x-request-id" content="<?= h($rid) ?>" /> <!-- Favicons / PWA --> <?php if ($faviconIco !== ''): ?> <link rel="icon" href="<?= h($faviconIco) ?>"> <?php endif; ?> <?php if ($favicon16 !== ''): ?> <link rel="icon" type="image/png" sizes="16x16" href="<?= h($favicon16) ?>"> <?php endif; ?> <?php if ($favicon32 !== ''): ?> <link rel="icon" type="image/png" sizes="32x32" href="<?= h($favicon32) ?>"> <?php endif; ?> <?php if ($apple180 !== ''): ?> <link rel="apple-touch-icon" sizes="180x180" href="<?= h($apple180) ?>"> <?php endif; ?> <?php if ($manifest !== ''): ?> <link rel="manifest" href="<?= h($manifest) ?>"> <?php endif; ?> <?php if ($browserconf !== ''): ?> <meta name="msapplication-config" content="<?= h($browserconf) ?>"> <?php endif; ?> <meta name="msapplication-TileColor" content="<?= h($tileColor) ?>"> <?php if ($GA_ID !== ''): ?> <!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=<?= h($GA_ID) ?>"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', <?= json_encode($GA_ID) ?>, { send_page_view: true }); // SIMON context try { gtag('set', 'user_properties', { simon: 'active' }); } catch(e){} </script> <?php endif; ?> <style> :root{ --bg:#070b16; --panel:rgba(255,255,255,.06); --panel2:rgba(255,255,255,.03); --edge:rgba(255,255,255,.14); --text:#eaf2ff; --muted:#a9b6d9; --cyan:#6ae0ff; --indigo:#7b62ff; --pink:#ff4fd6; --radius:18px; --shadow: 0 18px 60px rgba(0,0,0,.55); } *{box-sizing:border-box} html,body{height:100%} body{ margin:0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; color:var(--text); background: radial-gradient(1200px 800px at 50% 20%, rgba(123,98,255,.14), transparent 60%), radial-gradient(900px 600px at 70% 60%, rgba(106,224,255,.10), transparent 55%), linear-gradient(180deg, #050812, var(--bg)); } .gs-topbar{ position:sticky; top:0; z-index:1000; display:flex; align-items:center; justify-content:space-between; padding: 14px 16px; border-bottom:1px solid rgba(255,255,255,.10); background: linear-gradient(180deg, rgba(10,14,30,.78), rgba(7,11,22,.56)); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); } .gs-left{display:flex; align-items:baseline; gap:12px; min-width: 260px;} .gs-brand{ font-weight: 900; letter-spacing: .18em; text-transform: uppercase; font-size: 13px; color: rgba(234,242,255,.90); text-decoration:none; white-space:nowrap; } .gs-tag{ font-size: 12px; color: rgba(169,182,217,.78); letter-spacing:.06em; text-transform: uppercase; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width: min(52vw, 720px); } .gs-right{display:flex; align-items:center; gap:10px;} .gs-btn{ border:1px solid rgba(255,255,255,.14); background: linear-gradient(135deg, rgba(255,255,255,.06), rgba(255,255,255,.02)); color: rgba(234,242,255,.92); border-radius: 999px; padding: 10px 12px; cursor:pointer; font-weight: 750; letter-spacing:.06em; text-transform: uppercase; font-size: 12px; display:inline-flex; align-items:center; gap:8px; } .gs-btn:hover{ border-color: rgba(106,224,255,.26); } .gs-dd{ position:relative; } .gs-panel{ position:absolute; right:0; top: calc(100% + 10px); width: min(860px, calc(100vw - 28px)); max-height: min(72vh, 640px); overflow:auto; border-radius: calc(var(--radius) + 6px); border:1px solid rgba(255,255,255,.14); background: linear-gradient(180deg, rgba(255,255,255,.075), rgba(255,255,255,.03)); box-shadow: var(--shadow); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); padding: 16px; display:none; } .gs-panel.on{ display:block; } .gs-panel .top{ display:flex; gap:10px; align-items:center; justify-content:space-between; margin-bottom: 12px; } .gs-search{ width: 100%; border-radius: 14px; border:1px solid rgba(255,255,255,.14); background: rgba(0,0,0,.18); color: rgba(234,242,255,.92); padding: 10px 12px; outline:none; font-size: 14px; } .gs-grid{ display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; } @media (max-width: 860px){ .gs-left{min-width: 0;} .gs-grid{ grid-template-columns: 1fr; } .gs-tag{ display:none; } } .gs-group{ border-radius: var(--radius); border:1px solid rgba(255,255,255,.10); background: linear-gradient(180deg, rgba(0,0,0,.18), rgba(0,0,0,.06)); padding: 14px; min-height: 120px; } .gs-group h3{ margin:0 0 10px 0; font-size: 12px; letter-spacing:.14em; text-transform: uppercase; color: rgba(234,242,255,.86); } .gs-link{ display:flex; align-items:center; justify-content:space-between; gap:10px; text-decoration:none; color: rgba(234,242,255,.92); border:1px solid rgba(255,255,255,.12); background: linear-gradient(135deg, rgba(255,255,255,.06), rgba(255,255,255,.02)); border-radius: 14px; padding: 10px 10px; margin: 8px 0; transition: transform 160ms ease, border-color 160ms ease, background 160ms ease; } .gs-link:hover{ transform: translateY(-1px); border-color: rgba(106,224,255,.22); background: linear-gradient(135deg, rgba(106,224,255,.10), rgba(255,255,255,.02)); } .gs-small{ color: rgba(169,182,217,.82); font-size: 12px; line-height: 1.3; margin-top: -6px; margin-bottom: 6px; } .gs-icons{ display:flex; gap:10px; align-items:center; } .gs-icon{ color: rgba(234,242,255,.70); text-decoration:none; font-size: 12px; border:1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.05); padding: 9px 10px; border-radius: 999px; } .gs-icon:hover{ border-color: rgba(106,224,255,.22); } .gs-page{ width: min(1100px, calc(100vw - 32px)); margin: 22px auto 60px auto; } .gs-foot{ margin-top: 40px; padding: 14px 0 0 0; border-top: 1px solid rgba(255,255,255,.10); color: rgba(169,182,217,.80); font-size: 12px; } .gs-foot b{ color: rgba(234,242,255,.88); } </style> </head> <body> <header class="gs-topbar" role="banner"> <div class="gs-left"> <a class="gs-brand" href="/"><?= h($SITE['brand']['name']) ?></a> <div class="gs-tag"><?= h($SITE['brand']['tagline']) ?></div> </div> <div class="gs-right"> <?php if (!empty($SOCIAL) && is_array($SOCIAL)): ?> <div class="gs-icons" aria-label="Social links"> <?php foreach ($SOCIAL as $label => $url): ?> <?php if (!is_string($url) || $url === '') continue; ?> <a class="gs-icon" href="<?= h($url) ?>" target="_blank" rel="noopener"><?= h((string)$label) ?></a> <?php endforeach; ?> </div> <?php endif; ?> <div class="gs-dd"> <button class="gs-btn" id="gsNavBtn" type="button" aria-haspopup="true" aria-expanded="false"> Destinations <span aria-hidden="true">▾</span> </button> <div class="gs-panel" id="gsNavPanel" role="menu" aria-label="Site destinations"> <div class="top"> <input class="gs-search" id="gsNavSearch" type="search" placeholder="Quick jump… (type: book, art, infinity, simon…)" autocomplete="off" /> </div> <div class="gs-grid" id="gsNavGrid"> <?php foreach ($NAV as $groupName => $items): ?> <section class="gs-group" data-group="<?= h((string)$groupName) ?>"> <h3><?= h((string)$groupName) ?></h3> <?php foreach ($items as $item): ?> <?php $label = (string)($item[0] ?? ''); $href = (string)($item[1] ?? '#'); $desc = (string)($item[2] ?? ''); $hay = strtolower(trim($label.' '.$desc.' '.$groupName)); ?> <a class="gs-link" href="<?= h($href) ?>" role="menuitem" data-label="<?= h($hay) ?>" data-simon-link="1" data-simon-dest="<?= h($label) ?>"> <span><?= h($label) ?></span><i aria-hidden="true">→</i> </a> <?php if ($desc !== ''): ?> <div class="gs-small"><?= h($desc) ?></div> <?php endif; ?> <?php endforeach; ?> </section> <?php endforeach; ?> </div> </div> </div> </div> </header> <main class="gs-page" role="main"> <?php } function page_footer(): void { $rid = gs_request_id(); ?> <div class="gs-foot"> <b>Powered by SIMON</b> • Request ID: <code><?= h($rid) ?></code> • Logged </div> </main> <script> (() => { const btn = document.getElementById('gsNavBtn'); const panel = document.getElementById('gsNavPanel'); const search = document.getElementById('gsNavSearch'); if (!btn || !panel) return; function openPanel(){ panel.classList.add('on'); btn.setAttribute('aria-expanded','true'); setTimeout(() => search && search.focus(), 0); } function closePanel(){ panel.classList.remove('on'); btn.setAttribute('aria-expanded','false'); if (search) { search.value=''; filter(''); } } function toggle(){ panel.classList.contains('on') ? closePanel() : openPanel(); } btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); toggle(); }); document.addEventListener('pointerdown', (e) => { if (!panel.classList.contains('on')) return; if (panel.contains(e.target) || btn.contains(e.target)) return; closePanel(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePanel(); }); function filter(q){ q = (q || '').trim().toLowerCase(); const links = panel.querySelectorAll('.gs-link'); const groups = panel.querySelectorAll('.gs-group'); links.forEach(a => { const hay = (a.getAttribute('data-label') || ''); const ok = !q || hay.includes(q); a.style.display = ok ? '' : 'none'; const small = a.nextElementSibling && a.nextElementSibling.classList.contains('gs-small') ? a.nextElementSibling : null; if (small) small.style.display = ok ? '' : 'none'; }); groups.forEach(g => { const anyVisible = Array.from(g.querySelectorAll('.gs-link')).some(a => a.style.display !== 'none'); g.style.display = anyVisible ? '' : 'none'; }); } if (search) search.addEventListener('input', () => filter(search.value)); // GTAG click events for nav links (optional but useful) document.addEventListener('click', (e) => { const a = e.target && e.target.closest ? e.target.closest('a[data-simon-link="1"]') : null; if (!a) return; const label = a.getAttribute('data-simon-dest') || a.href; try{ if (typeof gtag === 'function'){ gtag('event', 'nav_click', { event_category:'navigation', event_label: label }); } }catch(_){} // also log locally (SIMON Log Intelligence) try{ // fire-and-forget beacon to /log.php if you add it later // (kept as placeholder; doesn’t break if missing) if (navigator.sendBeacon){ const payload = JSON.stringify({evt:'nav_click', label: label, href: a.getAttribute('href') || ''}); navigator.sendBeacon('/log.php', payload); } }catch(_){} }); })(); </script> </body> </html> <?php }
Save file
Quick jump
open a path
Open