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,267
Folders
403
Scanned Size
3.84 GB
PHP Files
863
Editable Text Files
12,530
File viewer
guarded to /htdocs
/connlink/test1/ui/index.php
<?php declare(strict_types=1); require_once __DIR__ . '/bootstrap.php'; $feeds = simon_read_json_file(SIMON_FEEDS_FILE, []); $enabled = is_array($feeds) ? count(array_filter($feeds, fn($f)=>is_array($f)&&!empty($f['enabled']))) : 0; ?> <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><title>SIMON Live · Mission Control</title> <style> :root{--bg:#050510;--panel:#12121a;--panel2:#1b1b25;--line:rgba(255,255,255,.1);--text:#f7f7fb;--muted:#9da0af;--pink:#ff4f88;--cyan:#50e5ff;--vio:#8a68ff;--green:#5cf0a8;--red:#ff6f91;--amber:#ffc45b}*{box-sizing:border-box}body{margin:0;background:radial-gradient(900px 600px at 60% 0,rgba(138,104,255,.16),transparent),#050510;color:var(--text);font:14px/1.45 system-ui,-apple-system,Segoe UI,sans-serif}.app{display:grid;grid-template-columns:250px 1fr;min-height:100vh}.side{position:sticky;top:0;height:100vh;background:linear-gradient(180deg,#11111a,#090910);border-right:1px solid var(--line);padding:22px 14px;overflow:auto}.brand{font-size:22px;font-weight:950;padding:0 10px 18px}.brand small{display:block;color:var(--muted);font-size:10px;letter-spacing:.16em}.nav{display:grid;gap:4px}.nav button,.nav a{border:0;background:transparent;color:var(--text);padding:10px 12px;border-radius:10px;text-align:left;text-decoration:none;cursor:pointer;font:inherit}.nav button:hover,.nav a:hover,.nav button.on{background:rgba(255,255,255,.07)}.nav button.on{background:linear-gradient(100deg,var(--pink),#d93a73)}.sys{border-top:1px solid var(--line);margin-top:18px;padding-top:14px}.main{padding:26px 30px 80px;min-width:0}.top{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.top h1{margin:0;font-size:34px}.grow{flex:1}.search{min-width:240px;background:#171721;color:#fff;border:1px solid var(--line);border-radius:999px;padding:11px 15px}.btn{border:1px solid var(--line);background:#171721;color:#fff;border-radius:999px;padding:10px 14px;cursor:pointer;text-decoration:none}.btn.hot{background:linear-gradient(100deg,var(--cyan),var(--vio));color:#080816;font-weight:900}.status{color:var(--muted);font-size:12px}.stats{display:grid;grid-template-columns:repeat(5,minmax(120px,1fr));gap:10px;margin:18px 0}.stat{background:linear-gradient(180deg,#171722,#101018);border:1px solid var(--line);border-radius:16px;padding:14px}.stat .n{font-size:28px;font-weight:950}.stat .k{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.1em}.toolbar{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0 18px}.chip{border:1px solid var(--line);background:#15151f;color:#fff;border-radius:999px;padding:7px 11px;cursor:pointer}.chip.on{border-color:var(--cyan);color:var(--cyan)}.layout{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:18px}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px}.card{position:relative;background:#161620;border:1px solid var(--line);border-radius:18px;overflow:hidden;min-height:300px;display:flex;flex-direction:column}.media{aspect-ratio:16/9;background:linear-gradient(135deg,#281746,#112c43);position:relative;overflow:hidden}.media img{width:100%;height:100%;object-fit:cover;display:block}.fallback{position:absolute;inset:0;display:grid;place-items:center;font-size:54px}.badge{position:absolute;top:10px;left:10px;background:rgba(0,0,0,.72);padding:4px 8px;border-radius:999px;font-size:10px;text-transform:uppercase}.body{padding:13px 14px 16px;display:flex;flex-direction:column;flex:1}.source{font-size:10px;color:var(--cyan);text-transform:uppercase;letter-spacing:.1em}.title{font-size:17px;font-weight:850;line-height:1.22;margin:7px 0}.summary{color:var(--muted);font-size:12px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.foot{display:flex;justify-content:space-between;gap:8px;margin-top:auto;padding-top:12px;color:var(--muted);font-size:11px}.card a.cover{position:absolute;inset:0;z-index:4}.panel{background:#12121a;border:1px solid var(--line);border-radius:18px;padding:15px;margin-bottom:14px}.panel h2{margin:0 0 10px;font-size:16px}.health{display:grid;gap:7px;max-height:430px;overflow:auto}.hrow{display:flex;gap:8px;justify-content:space-between;padding:8px;border-radius:10px;background:#181822;font-size:12px}.ok{color:var(--green)}.bad{color:var(--red)}.empty{border:1px dashed var(--line);border-radius:16px;padding:30px;text-align:center;color:var(--muted);grid-column:1/-1}.weatherbox{background:linear-gradient(135deg,#17344d,#2f1b58);border-radius:16px;padding:14px}.weatherbox .temp{font-size:42px;font-weight:950}.socialnote{font-size:12px;color:var(--muted)}.mobile{display:none}.toast{position:fixed;right:18px;bottom:18px;background:#11111a;border:1px solid var(--line);padding:10px 14px;border-radius:12px;opacity:0;transition:.2s}.toast.on{opacity:1}@media(max-width:1000px){.layout{grid-template-columns:1fr}.right{order:-1}.stats{grid-template-columns:repeat(3,1fr)}}@media(max-width:760px){.app{display:block}.side{display:none}.main{padding:18px 14px 88px}.stats{grid-template-columns:repeat(2,1fr)}.top h1{font-size:28px}.search{width:100%}.mobile{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:20;background:#11111a;border-top:1px solid var(--line);justify-content:space-around;padding:9px}.mobile button,.mobile a{color:#fff;background:none;border:0;text-decoration:none;font-size:11px}} </style></head><body><div class="app"><aside class="side"><div class="brand">SIMON Live<small>MISSION CONTROL · V3.1</small></div><div class="nav" id="nav"></div><div class="nav sys"><a href="news_reader.php">▤ SIMON Daily</a><a href="ai_dashboard.php">✦ AI Layer</a><a href="graph_dashboard.php">◉ Graph</a><a href="jobs_dashboard.php">↻ Jobs</a><a href="feed_manager.php">⚙ Feed Manager</a><a href="system_check.php">🛠 System Check</a><a href="troubleshoot.php">🧰 Troubleshoot</a></div></aside><main class="main"><header class="top"><div><h1 id="heading">Live Intelligence</h1><div class="status" id="status">Connecting…</div></div><span class="grow"></span><input class="search" id="q" type="search" placeholder="Search signals"><button class="btn" id="geo">📍 Weather for me</button><button class="btn hot" id="refresh">Refresh</button></header><section class="stats"><div class="stat"><div class="n"><?=$enabled?></div><div class="k">Sources</div></div><div class="stat"><div class="n" id="stItems">—</div><div class="k">Signals</div></div><div class="stat"><div class="n ok" id="stOnline">—</div><div class="k">Online</div></div><div class="stat"><div class="n bad" id="stFailed">—</div><div class="k">Failed</div></div><div class="stat"><div class="n" id="stMs">—</div><div class="k">Load ms</div></div></section><div class="toolbar" id="chips"></div><div class="layout"><section><div class="grid" id="items"><div class="empty">Loading…</div></div></section><aside class="right"><div class="panel"><h2>Weather for you</h2><div id="weather"><div class="socialnote">Tap “Weather for me” and allow location. Until then, SIMON uses the configured Dallas feed.</div></div></div><div class="panel"><h2>Social connections</h2><div class="socialnote">Threads, Instagram, Facebook, TikTok and X are profile connections. Substack imports posts now. Official OAuth is required for automatic posts from the other five.</div><button class="btn" style="margin-top:10px" data-cat="social">Open Social</button></div><div class="panel"><h2>Provider health</h2><div class="health" id="health"><div class="empty">Waiting…</div></div></div></aside></div></main></div><nav class="mobile"><button data-cat="">◉<br>Home</button><button data-cat="news">▤<br>News</button><button data-cat="social">◎<br>Social</button><button id="mgeo">📍<br>Weather</button><a href="feed_manager.php">⚙<br>Feeds</a></nav><div class="toast" id="toast"></div> <script> const CATS=[['','◉ All Intelligence'],['news','📰 News'],['world','🌍 World'],['ai','✦ AI'],['technology','⌘ Technology'],['science','⚗ Science'],['space','◌ Space'],['weather','☁ Weather'],['earth','◈ Earth'],['security','⌾ Security'],['government','🏛 Government'],['finance','$ Finance'],['entertainment','★ Entertainment'],['social','◎ Social']]; const S={cat:'',q:'',items:[],health:[],geo:null}; const $=id=>document.getElementById(id); const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); function icon(cat,engine){return ({news:'📰',world:'🌍',ai:'✦',technology:'💻',science:'⚗️',space:'🛰️',weather:'⛅',earth:'🌋',security:'🛡️',government:'🏛️',finance:'💹',entertainment:'🎬',social:'◎',simon:'🧠'}[cat]||({social:'◎',rss:'📰',weather:'⛅'}[engine]||'✦'))} function toast(s){$('toast').textContent=s;$('toast').classList.add('on');setTimeout(()=>$('toast').classList.remove('on'),2200)} function buildNav(){const nav=$('nav');nav.innerHTML='';CATS.forEach(([k,l])=>{const b=document.createElement('button');b.dataset.cat=k;b.textContent=l;b.className=S.cat===k?'on':'';b.onclick=()=>selectCat(k);nav.appendChild(b)});document.querySelectorAll('[data-cat]').forEach(b=>b.onclick=()=>selectCat(b.dataset.cat||''))} function selectCat(cat){S.cat=cat;buildNav();$('heading').textContent=CATS.find(x=>x[0]===cat)?.[1].replace(/^\S+\s/,'')||'Live Intelligence';load(false)} function endpoint(force=false){const p=new URLSearchParams({limit:S.cat==='social'?'100':'250'});if(S.cat)p.set('category',S.cat);if(S.q)p.set('q',S.q);if(force)p.set('force','1');if(S.geo){p.set('lat',S.geo.lat);p.set('lon',S.geo.lon);p.set('place',S.geo.place||'My location')}return 'feed_proxy.php?'+p} async function load(force=false){$('status').textContent='Loading…';$('items').innerHTML='<div class="empty">Loading normalized events…</div>';const t=performance.now();try{const r=await fetch(endpoint(force),{cache:'no-store'});if(!r.ok)throw new Error('HTTP '+r.status);const j=await r.json();if(!j.ok)throw new Error(j.error||'API error');S.items=j.items||[];S.health=j.health||[];$('stItems').textContent=S.items.length;$('stOnline').textContent=S.health.filter(x=>x.ok).length;$('stFailed').textContent=S.health.filter(x=>!x.ok).length;$('stMs').textContent=j.elapsedMs||Math.round(performance.now()-t);$('status').textContent=`${j.version} · ${new Date(j.checkedAt).toLocaleString()}`;renderChips(j.categories||{});renderItems();renderHealth();renderWeather()}catch(e){$('status').innerHTML='<span class="bad">'+esc(e.message)+'</span>';$('items').innerHTML='<div class="empty">Could not read feed_proxy.php. Open System Check.</div>'}} function renderChips(cats){const h=$('chips');h.innerHTML='';Object.entries(cats).forEach(([k,n])=>{const b=document.createElement('button');b.className='chip'+(S.cat===k?' on':'');b.textContent=`${k} ${n}`;b.onclick=()=>selectCat(k);h.appendChild(b)})} function imgUrl(it){if(it.image)return it.image;if(it.url&&it.url!=='#')return 'image_proxy.php?url='+encodeURIComponent(it.url);return ''} function renderItems(){const h=$('items');h.innerHTML='';if(!S.items.length){h.innerHTML='<div class="empty">No items in this view. For social, profile connections and Substack should appear here.</div>';return}S.items.forEach(it=>{const d=document.createElement('article');d.className='card';const image=imgUrl(it);d.innerHTML=`<div class="media"><div class="fallback">${icon(it.category,it.engine)}</div>${image?`<img loading="lazy" src="${esc(image)}" alt="" onerror="this.remove()">`:''}<span class="badge">${esc(it.category)}</span></div><div class="body"><div class="source">${esc(it.source)}</div><div class="title">${esc(it.title)}</div><div class="summary">${esc(it.summary||'')}</div><div class="foot"><span>${new Date(it.published).toLocaleString()}</span><span>${it.score??''}</span></div></div>${it.url&&it.url!=='#'?`<a class="cover" href="${esc(it.url)}" target="_blank" rel="noopener" aria-label="Open"></a>`:''}`;h.appendChild(d)})} function renderHealth(){const h=$('health');h.innerHTML='';S.health.forEach(x=>{const d=document.createElement('div');d.className='hrow';d.innerHTML=`<span>${esc(x.name)}</span><b class="${x.ok?'ok':'bad'}">${x.ok?'● '+x.count:'✕'}</b>`;h.appendChild(d)})} function renderWeather(){const w=S.items.find(x=>x.category==='weather'&&x.engine==='weather')||null;if(!w)return;const raw=w.raw?.raw||w.raw||{};const c=raw.current||{};$('weather').innerHTML=`<div class="weatherbox"><div>${esc(raw.place||w.source)}</div><div class="temp">${Math.round(c.temperature_2m??0)}°F</div><div>${esc(w.summary)}</div></div>`} async function useGeo(){if(!navigator.geolocation){toast('Geolocation unavailable');return}toast('Requesting location…');navigator.geolocation.getCurrentPosition(async p=>{let place='My location';try{const r=await fetch(`https://geocoding-api.open-meteo.com/v1/reverse?latitude=${p.coords.latitude}&longitude=${p.coords.longitude}&count=1`);const j=await r.json();const x=j.results?.[0];if(x)place=[x.name,x.admin1].filter(Boolean).join(', ')}catch{}S.geo={lat:p.coords.latitude,lon:p.coords.longitude,place};localStorage.setItem('simon_geo',JSON.stringify(S.geo));toast('Weather set to '+place);if(S.cat!=='weather')S.cat='weather';buildNav();load(true)},e=>toast('Location blocked: '+e.message),{timeout:10000,maximumAge:300000})} $('q').oninput=e=>{S.q=e.target.value.trim();clearTimeout(window.qt);window.qt=setTimeout(()=>load(false),350)};$('refresh').onclick=()=>load(true);$('geo').onclick=useGeo;$('mgeo').onclick=useGeo;try{S.geo=JSON.parse(localStorage.getItem('simon_geo')||'null')}catch{}buildNav();load(false); </script></body></html>
Save file
Quick jump
open a path
Open