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,360
Folders
427
Scanned Size
3.85 GB
PHP Files
948
Editable Text Files
12,616
File viewer
guarded to /htdocs
/live/graph_dashboard.php
<?php declare(strict_types=1); ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>SIMON · Intelligence Graph</title> <style> :root{--bg:#05041a;--panel:rgba(16,14,48,.78);--line:rgba(75,227,255,.26);--text:#eaf2ff;--muted:#9aa3c4;--cyan:#4be3ff;--vio:#7a5cff;--pink:#ff5bd1;--green:#7cff9e;--amber:#ffb347;--red:#ff6b8f} *{box-sizing:border-box}body{margin:0;min-height:100vh;color:var(--text);font:14px/1.45 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:radial-gradient(900px 600px at 20% 5%,rgba(122,92,255,.22),transparent 55%),radial-gradient(900px 600px at 90% 20%,rgba(75,227,255,.14),transparent 55%),#05041a;overflow:hidden}.top{height:64px;display:flex;align-items:center;gap:12px;padding:12px 18px;border-bottom:1px solid rgba(255,255,255,.08);background:rgba(5,4,26,.78);backdrop-filter:blur(12px)}.logo{width:34px;height:34px;border-radius:11px;background:conic-gradient(from 220deg,var(--vio),var(--cyan),var(--pink),var(--vio));box-shadow:0 0 24px rgba(75,227,255,.35)}h1{font-size:16px;margin:0;letter-spacing:.12em;text-transform:uppercase}.sub{color:var(--muted);font-size:12px}.grow{flex:1}.btn,.select,.input{border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.04);color:var(--text);border-radius:999px;padding:9px 12px;font:inherit}.btn{cursor:pointer;background:linear-gradient(135deg,var(--cyan),var(--vio));color:#08051d;font-weight:800}.btn.ghost{background:transparent;color:var(--text)}.shell{display:grid;grid-template-columns:1fr 360px;height:calc(100vh - 64px)}#graph{position:relative;overflow:hidden}.side{border-left:1px solid rgba(255,255,255,.08);background:rgba(8,6,30,.86);backdrop-filter:blur(14px);padding:16px;overflow:auto}.card{border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);border-radius:16px;padding:14px;margin-bottom:12px}.k{font-size:11px;color:var(--muted);letter-spacing:.12em;text-transform:uppercase}.v{font-size:24px;font-weight:900}.legend{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}.chip{display:flex;gap:8px;align-items:center;color:var(--muted);font-size:12px}.dot{width:10px;height:10px;border-radius:50%}.node{position:absolute;border-radius:999px;display:grid;place-items:center;text-align:center;padding:8px;border:1px solid rgba(255,255,255,.18);box-shadow:0 8px 28px rgba(0,0,0,.38);cursor:pointer;user-select:none;transition:transform .16s ease,box-shadow .16s ease,opacity .2s ease;overflow:hidden}.node:hover{transform:translate(-50%,-50%) scale(1.12)!important;box-shadow:0 18px 48px rgba(0,0,0,.55),0 0 28px rgba(75,227,255,.32);z-index:5}.node span{display:block;max-width:120px;overflow:hidden;text-overflow:ellipsis;font-weight:800;font-size:11px}.edge{position:absolute;height:1px;background:linear-gradient(90deg,rgba(75,227,255,.0),rgba(75,227,255,.38),rgba(122,92,255,.0));transform-origin:0 50%;pointer-events:none}.details h2{margin:4px 0 8px;font-size:18px}.details p{color:var(--muted);margin:8px 0}.pill{display:inline-flex;margin:3px;padding:4px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.1);font-size:11px;color:var(--muted)}.list{display:grid;gap:8px}.row{padding:10px;border-radius:12px;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.03);cursor:pointer}.row:hover{border-color:var(--line)}@media(max-width:900px){.shell{grid-template-columns:1fr}.side{position:fixed;right:12px;bottom:12px;left:12px;max-height:42vh;border:1px solid rgba(255,255,255,.1);border-radius:18px}.top{flex-wrap:wrap;height:auto}.input{max-width:160px}} </style> </head><body> <div class="top"><div class="logo"></div><div><h1>SIMON Intelligence Graph</h1><div class="sub">Phase 4 · Events → topics → entities → sources → time</div></div><span class="grow"></span><input id="q" class="input" placeholder="Search graph"><select id="type" class="select"><option value="">All nodes</option><option>event</option><option>entity</option><option>keyword</option><option>category</option><option>source</option><option>domain</option><option>location</option><option>time</option><option>sentiment</option></select><button class="btn ghost" id="rebuild">Rebuild</button><button class="btn" id="load">Load</button></div> <div class="shell"><main id="graph"></main><aside class="side"><div class="card"><div class="k">Graph Stats</div><div class="v" id="statNodes">—</div><div class="sub" id="statSub">loading…</div></div><div class="card"><div class="k">Legend</div><div class="legend" id="legend"></div></div><div class="card details" id="details"><div class="k">Selected</div><h2>Click a node</h2><p>Connections will appear here.</p></div><div class="card"><div class="k">Top Nodes</div><div class="list" id="topNodes"></div></div></aside></div> <script> const COLORS={system:'#ffffff',event:'#4be3ff',entity:'#ff5bd1',keyword:'#7cff9e',category:'#7a5cff',source:'#ffb347',domain:'#34f0c8',location:'#ff6b8f',time:'#9aa3c4',sentiment:'#d6b8ff'}; let GRAPH={nodes:[],edges:[],stats:{}}; const graphEl=document.getElementById('graph'); function esc(s){return String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))} async function loadGraph(fresh=false){const q=document.getElementById('q').value.trim();const type=document.getElementById('type').value;let url='graph_api.php?limit=700'+(fresh?'&fresh=1':'')+(q?'&q='+encodeURIComponent(q):'')+(type?'&type='+encodeURIComponent(type):'');const r=await fetch(url,{cache:'no-store'});GRAPH=await r.json();render();} function render(){renderStats();renderLegend();renderGraph();renderTop();} function renderStats(){document.getElementById('statNodes').textContent=(GRAPH.nodes||[]).length+' nodes';document.getElementById('statSub').textContent=(GRAPH.edges||[]).length+' edges · '+((GRAPH.stats||{}).events||0)+' events';} function renderLegend(){const h=document.getElementById('legend');h.innerHTML='';Object.entries(COLORS).forEach(([k,c])=>{const d=document.createElement('div');d.className='chip';d.innerHTML=`<span class="dot" style="background:${c};box-shadow:0 0 10px ${c}"></span>${k}`;h.appendChild(d);});} function layout(nodes){const W=graphEl.clientWidth,H=graphEl.clientHeight,cx=W/2,cy=H/2;const groups={};nodes.forEach(n=>(groups[n.type]??=[]).push(n));const order=Object.keys(groups);let pos={};order.forEach((type,gi)=>{const arr=groups[type],rad=type==='event'?Math.min(W,H)*.34:Math.min(W,H)*(.16+.04*(gi%5));arr.forEach((n,i)=>{const a=(i/Math.max(1,arr.length))*Math.PI*2+(gi*.55);pos[n.id]={x:cx+Math.cos(a)*rad+(Math.random()-.5)*26,y:cy+Math.sin(a)*rad+(Math.random()-.5)*26};});});if(pos['simon:core'])pos['simon:core']={x:cx,y:cy};return pos;} function renderGraph(){graphEl.innerHTML='';const nodes=(GRAPH.nodes||[]).slice(0,380);const ids=new Set(nodes.map(n=>n.id));const edges=(GRAPH.edges||[]).filter(e=>ids.has(e.from)&&ids.has(e.to)).slice(0,650);const pos=layout(nodes);edges.forEach(e=>{const a=pos[e.from],b=pos[e.to];if(!a||!b)return;const dx=b.x-a.x,dy=b.y-a.y,len=Math.hypot(dx,dy);const el=document.createElement('div');el.className='edge';el.style.left=a.x+'px';el.style.top=a.y+'px';el.style.width=len+'px';el.style.opacity=Math.min(.8,.12+(e.weight||1)/18);el.style.transform=`rotate(${Math.atan2(dy,dx)}rad)`;graphEl.appendChild(el);});nodes.forEach(n=>{const p=pos[n.id];if(!p)return;const size=Math.max(34,Math.min(110,28+Math.sqrt(n.score||1)*10));const el=document.createElement('div');el.className='node';el.style.left=p.x+'px';el.style.top=p.y+'px';el.style.width=size+'px';el.style.height=size+'px';el.style.transform='translate(-50%,-50%)';const c=COLORS[n.type]||'#fff';el.style.background=`radial-gradient(circle at 35% 30%, ${c}55, #12103a 70%)`;el.style.borderColor=c+'88';el.innerHTML=`<span>${esc(n.label)}</span>`;el.onclick=()=>selectNode(n);graphEl.appendChild(el);});} async function selectNode(n){const d=document.getElementById('details');d.innerHTML=`<div class="k">${esc(n.type)}</div><h2>${esc(n.label)}</h2><p>${esc(n.meta?.summary||n.meta?.title||n.meta?.description||'')}</p><div><span class="pill">count ${n.count||0}</span><span class="pill">score ${Math.round(n.score||0)}</span></div><p>Loading neighbors…</p>`;try{const r=await fetch('graph_api.php?action=neighbors&id='+encodeURIComponent(n.id));const j=await r.json();d.innerHTML+=`<div class="k" style="margin-top:12px">Neighbors</div>`+(j.edges||[]).slice(0,20).map(e=>`<div class="row"><b>${esc(e.type)}</b><br><span class="sub">${esc(e.from)} → ${esc(e.to)}</span></div>`).join('');}catch{}} function renderTop(){const h=document.getElementById('topNodes');h.innerHTML='';(GRAPH.nodes||[]).slice(0,16).forEach(n=>{const r=document.createElement('div');r.className='row';r.innerHTML=`<b>${esc(n.label)}</b><br><span class="sub">${esc(n.type)} · score ${Math.round(n.score||0)} · count ${n.count||0}</span>`;r.onclick=()=>selectNode(n);h.appendChild(r);});} document.getElementById('load').onclick=()=>loadGraph(false);document.getElementById('rebuild').onclick=()=>loadGraph(true);document.getElementById('q').addEventListener('keydown',e=>{if(e.key==='Enter')loadGraph(false)});document.getElementById('type').onchange=()=>loadGraph(false);loadGraph(false); </script></body></html>
Save file
Quick jump
open a path
Open