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,526
Folders
410
Scanned Size
3.85 GB
PHP Files
894
Editable Text Files
12,787
File viewer
guarded to /htdocs
/connlink/test1/ui/old/link_command_center.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>SIMON Intelligence — LINK Command Center</title> <style> :root{ --bg:#05030f; --bg-2:#0a0720; --panel:rgba(18,12,45,0.68); --panel-2:rgba(10,7,28,0.82); --line:rgba(122,92,255,0.22); --line-2:rgba(75,227,255,0.18); --indigo:#7a5cff; --cyan:#4be3ff; --pink:#ff5bd1; --amber:#ffb347; --green:#6bffa0; --red:#ff6b8f; --text:#e9e6ff; --muted:#9992c2; } *{box-sizing:border-box} html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font:13px/1.45 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif} body{ background: radial-gradient(1200px 700px at 8% -10%, rgba(122,92,255,0.25), transparent 60%), radial-gradient(900px 600px at 110% 0%, rgba(75,227,255,0.16), transparent 55%), radial-gradient(900px 600px at 50% 120%, rgba(255,91,209,0.14), transparent 60%), linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%); overflow-x:hidden; } a{color:var(--cyan);text-decoration:none} /* ---- Top bar ---- */ .topbar{ display:flex;align-items:center;gap:14px; padding:14px 22px; border-bottom:1px solid var(--line); background:linear-gradient(180deg, rgba(10,7,28,0.9), rgba(10,7,28,0.4)); backdrop-filter: blur(8px); position:sticky;top:0;z-index:10; } .brand{display:flex;align-items:center;gap:10px;font-weight:700;letter-spacing:.04em} .brand .logo{ width:28px;height:28px;border-radius:8px; background:conic-gradient(from 210deg,var(--indigo),var(--cyan),var(--pink),var(--indigo)); box-shadow:0 0 24px rgba(122,92,255,.55); } .brand small{color:var(--muted);font-weight:500;letter-spacing:.18em;text-transform:uppercase;margin-left:6px} .grow{flex:1} .pill{ font-size:11px;padding:5px 9px;border-radius:999px; border:1px solid var(--line);background:rgba(122,92,255,.08);color:var(--text); display:inline-flex;align-items:center;gap:6px } .pill .dot{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 10px var(--green)} .pill.ok .dot{background:var(--green);box-shadow:0 0 10px var(--green)} .pill.warn .dot{background:var(--amber);box-shadow:0 0 10px var(--amber)} .pill.err .dot{background:var(--red);box-shadow:0 0 10px var(--red)} /* ---- Layout ---- */ main{ display:grid;grid-template-columns: 220px 1fr 360px;gap:16px; padding:16px 22px 40px; } @media (max-width: 1100px){ main{grid-template-columns:1fr} } .card{ background:var(--panel);border:1px solid var(--line);border-radius:14px; padding:14px;position:relative;overflow:hidden; } .card::before{ content:"";position:absolute;inset:-1px;border-radius:15px;pointer-events:none; background:linear-gradient(135deg, rgba(122,92,255,.35), rgba(75,227,255,.15) 40%, transparent 70%); -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; padding:1px; } .card h4{margin:0 0 10px;font-size:11px;letter-spacing:.18em;color:var(--muted);text-transform:uppercase} /* ---- Left sidebar ---- */ .side .catlist{display:flex;flex-direction:column;gap:4px} .side .cat{ display:flex;justify-content:space-between;align-items:center; padding:8px 10px;border-radius:8px;cursor:pointer; color:var(--text);border:1px solid transparent; } .side .cat:hover{background:rgba(122,92,255,.08);border-color:var(--line)} .side .cat.active{background:rgba(75,227,255,.10);border-color:var(--line-2)} .side .cat .n{color:var(--muted);font-variant-numeric:tabular-nums} /* ---- Stat row ---- */ .statrow{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:12px} @media (max-width: 900px){ .statrow{grid-template-columns:repeat(3,1fr)} } .stat{ padding:12px 14px;border-radius:12px;border:1px solid var(--line); background:linear-gradient(180deg, rgba(122,92,255,.10), rgba(10,7,28,.6)); } .stat .k{font-size:10px;letter-spacing:.2em;color:var(--muted);text-transform:uppercase} .stat .v{font-size:22px;font-weight:700;margin-top:4px;font-variant-numeric:tabular-nums} .stat.good .v{color:var(--green)} .stat.pend .v{color:var(--amber)} .stat.err .v{color:var(--red)} .stat.info .v{color:var(--cyan)} /* ---- Filter bar ---- */ .filters{display:flex;flex-wrap:wrap;gap:8px;margin:6px 0 14px} .chip{ padding:6px 10px;border-radius:999px;border:1px solid var(--line); background:rgba(10,7,28,.4);color:var(--text);cursor:pointer;font-size:12px; display:inline-flex;align-items:center;gap:6px } .chip:hover{border-color:var(--line-2)} .chip.active{background:rgba(75,227,255,.12);border-color:var(--cyan);color:#fff} .chip .dot{width:7px;height:7px;border-radius:50%;background:var(--muted)} .chip[data-status="connected"] .dot{background:var(--green)} .chip[data-status="pending"] .dot{background:var(--amber)} .chip[data-status="warning"] .dot{background:var(--amber)} .chip[data-status="broken"] .dot{background:var(--red)} .chip[data-status="planned"] .dot{background:var(--cyan)} /* ---- Provider grid ---- */ .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px} .prov{ position:relative;padding:14px;border-radius:12px;cursor:pointer; background:linear-gradient(180deg, rgba(18,12,45,.78), rgba(10,7,28,.72)); border:1px solid var(--line);transition:transform .12s ease, border-color .12s ease; } .prov:hover{transform:translateY(-1px);border-color:var(--line-2)} .prov.selected{border-color:var(--cyan);box-shadow:0 0 0 1px var(--cyan), 0 10px 40px rgba(75,227,255,.15)} .prov .head{display:flex;align-items:center;gap:10px;margin-bottom:10px} .prov .icon{ width:36px;height:36px;border-radius:10px;display:grid;place-items:center; font-weight:800;font-size:11px;letter-spacing:.04em;color:#0c0820; background:linear-gradient(135deg, var(--cyan), var(--indigo)); box-shadow:0 6px 22px rgba(122,92,255,.35) } .prov .name{font-weight:700} .prov .cat{font-size:11px;color:var(--muted)} .prov .desc{color:var(--muted);min-height:34px} .prov .meta{display:flex;justify-content:space-between;margin-top:10px;font-size:11px;color:var(--muted)} .prov .statusRow{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:11px} .prov .status-dot{width:8px;height:8px;border-radius:50%} .prov[data-status="connected"] .status-dot{background:var(--green);box-shadow:0 0 8px var(--green)} .prov[data-status="pending"] .status-dot{background:var(--amber);box-shadow:0 0 8px var(--amber)} .prov[data-status="warning"] .status-dot{background:var(--amber);box-shadow:0 0 8px var(--amber)} .prov[data-status="broken"] .status-dot{background:var(--red);box-shadow:0 0 8px var(--red)} .prov[data-status="planned"] .status-dot{background:var(--cyan);box-shadow:0 0 8px var(--cyan)} .prov .tier{ position:absolute;top:10px;right:10px;font-size:10px; padding:3px 7px;border-radius:999px;border:1px solid var(--line);color:var(--muted); text-transform:uppercase;letter-spacing:.12em } /* ---- Right panel ---- */ .detail .row{display:flex;justify-content:space-between;gap:8px;padding:6px 0;border-bottom:1px dashed var(--line)} .detail .row:last-child{border-bottom:none} .detail .k{color:var(--muted);font-size:11px;letter-spacing:.1em;text-transform:uppercase} .detail .v{color:var(--text);text-align:right;font-variant-numeric:tabular-nums} .detail .section{margin-top:12px} .detail .section h5{margin:0 0 6px;color:var(--muted);font-size:11px;letter-spacing:.18em;text-transform:uppercase} .detail ul{margin:0;padding-left:16px;color:var(--text)} .detail ul li{margin:2px 0} .bar{ height:8px;border-radius:999px;background:rgba(255,255,255,.05);overflow:hidden;margin-top:6px } .bar > span{display:block;height:100%;background:linear-gradient(90deg,var(--indigo),var(--cyan));} .btns{display:flex;gap:8px;margin-top:14px;flex-wrap:wrap} .btn{ padding:8px 12px;border-radius:10px;border:1px solid var(--line); background:rgba(122,92,255,.08);color:var(--text);cursor:pointer;font-size:12px } .btn:hover{border-color:var(--line-2)} .btn.primary{background:linear-gradient(135deg,var(--indigo),var(--cyan));color:#0c0820;border-color:transparent;font-weight:700} /* ---- Status states ---- */ .empty{color:var(--muted);padding:30px;text-align:center;border:1px dashed var(--line);border-radius:12px} /* ---- Search ---- */ .search{ flex:0 0 260px;display:flex;align-items:center;gap:8px; padding:7px 10px;border-radius:10px;border:1px solid var(--line); background:rgba(10,7,28,.5) } .search input{ flex:1;background:transparent;border:0;outline:0;color:var(--text);font:inherit } .search input::placeholder{color:var(--muted)} footer{color:var(--muted);padding:10px 22px;font-size:11px;border-top:1px solid var(--line);display:flex;gap:10px;flex-wrap:wrap} </style> </head> <body> <div class="topbar"> <div class="brand"> <div class="logo" aria-hidden="true"></div> <div>SIMON Intelligence <small>LINK · Command Center</small></div> </div> <div class="grow"></div> <div class="search" role="search"> <span style="color:var(--muted)">⌕</span> <input id="q" placeholder="Filter providers, categories, scopes…" /> </div> <span class="pill ok" id="healthPill"><span class="dot"></span><span id="healthTxt">Loading…</span></span> <span class="pill" id="clockPill"><span class="dot"></span><span id="clockTxt">—</span></span> </div> <main> <!-- Left sidebar --> <aside class="card side"> <h4>Categories</h4> <div class="catlist" id="catlist"> <div class="cat active" data-cat="__all__"><span>All providers</span><span class="n" id="catAll">0</span></div> </div> <h4 style="margin-top:18px">System</h4> <div class="catlist"> <div class="cat" data-goto="registry"><span>registry.json</span><span class="n">map</span></div> <div class="cat" data-goto="events"><span>carrier_events.ndjson</span><span class="n">log</span></div> <div class="cat" data-goto="env"><span>/web360/.env</span><span class="n">keys</span></div> </div> </aside> <!-- Center column --> <section> <div class="statrow"> <div class="stat info"><div class="k">Providers</div><div class="v" id="sTotal">0</div></div> <div class="stat good"><div class="k">Connected</div><div class="v" id="sConnected">0</div></div> <div class="stat pend"><div class="k">Pending</div><div class="v" id="sPending">0</div></div> <div class="stat err"><div class="k">Issues</div><div class="v" id="sIssues">0</div></div> <div class="stat info"><div class="k">Reqs / 24h</div><div class="v" id="sReq">0</div></div> <div class="stat good"><div class="k">Health</div><div class="v" id="sHealth">0%</div></div> </div> <div class="filters" id="filters"> <div class="chip active" data-status="__all__">All</div> <div class="chip" data-status="connected"><span class="dot"></span>Connected</div> <div class="chip" data-status="pending"><span class="dot"></span>Pending</div> <div class="chip" data-status="warning"><span class="dot"></span>Warning</div> <div class="chip" data-status="broken"><span class="dot"></span>Broken</div> <div class="chip" data-status="planned"><span class="dot"></span>Planned</div> </div> <div class="grid" id="grid"> <div class="empty">Loading provider registry…</div> </div> </section> <!-- Right panel --> <aside class="card detail" id="detail"> <h4 id="detailTitle">Provider Detail</h4> <div class="empty" id="detailEmpty">Select a provider to inspect.</div> <div id="detailBody" style="display:none"> <div class="row"><div class="k">Name</div><div class="v" id="dName">—</div></div> <div class="row"><div class="k">Category</div><div class="v" id="dCat">—</div></div> <div class="row"><div class="k">Tier</div><div class="v" id="dTier">—</div></div> <div class="row"><div class="k">Environment</div><div class="v" id="dEnv">—</div></div> <div class="row"><div class="k">Status</div><div class="v" id="dStatus">—</div></div> <div class="row"><div class="k">Connection</div><div class="v" id="dConn">—</div></div> <div class="row"><div class="k">Auth</div><div class="v" id="dAuth">—</div></div> <div class="row"><div class="k">Credential</div><div class="v" id="dCred">—</div></div> <div class="row"><div class="k">Last Sync</div><div class="v" id="dSync">—</div></div> <div class="row"><div class="k">Handshake</div><div class="v" id="dHand">—</div></div> <div class="row"><div class="k">Response</div><div class="v" id="dRt">—</div></div> <div class="row"><div class="k">Today</div><div class="v" id="dToday">— req / — err</div></div> <div class="section"> <h5>Validation</h5> <div id="dScore" style="font-weight:700">—</div> <div class="bar"><span id="dBar" style="width:0%"></span></div> </div> <div class="section"> <h5>Scopes</h5> <ul id="dScopes"></ul> </div> <div class="section"> <h5>Mapped files</h5> <ul id="dFiles"></ul> </div> <div class="section"> <h5>Dependencies</h5> <ul id="dDeps"></ul> </div> <div class="btns"> <button class="btn primary" id="dTestBtn">Test handshake</button> <button class="btn" id="dRegistryBtn">Open in registry</button> <button class="btn" id="dLogsBtn">Recent events</button> </div> </div> </aside> </main> <footer> <span>/web360/ui/link_command_center.html</span> <span>·</span> <span id="fSource">source: /web360/connlink/providers.php</span> <span>·</span> <span id="fUpdated">—</span> </footer> <script> /* ---------- Config ---------- */ const PROVIDERS_URL = '/web360/connlink/providers.php'; const CONN_BASE = '/web360/connlink'; /* ---------- State ---------- */ const state = { raw: null, providers: {}, filter: { status: '__all__', category: '__all__', q: '' }, selected: null, }; /* ---------- Utils ---------- */ const $ = (q, el=document) => el.querySelector(q); const $$ = (q, el=document) => [...el.querySelectorAll(q)]; const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const fmtInt = n => (Number(n||0)).toLocaleString(); function clockTick(){ const pill = $('#clockTxt'); const d = new Date(); pill.textContent = d.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit', second:'2-digit'}); } setInterval(clockTick, 1000); clockTick(); /* ---------- Fetch ---------- */ async function load(){ try{ const r = await fetch(PROVIDERS_URL, {cache:'no-store'}); if (!r.ok) throw new Error('HTTP '+r.status); const data = await r.json(); state.raw = data; state.providers = data.providers || {}; renderAll(); }catch(err){ $('#grid').innerHTML = `<div class="empty"> Could not load <code>${esc(PROVIDERS_URL)}</code> — ${esc(err.message)}. <br><br>Falling back to embedded registry preview. </div>`; // Fallback to embedded snapshot so the shell is still usable offline state.raw = FALLBACK; state.providers = FALLBACK.providers; renderAll(); } } /* ---------- Render ---------- */ function renderAll(){ renderStats(state.raw.stats || {}); renderCategories(state.raw.categories || []); renderGrid(); $('#fUpdated').textContent = 'updated: ' + (state.raw.checkedAt || '—'); const hp = state.raw.stats?.healthPct ?? 0; const pill = $('#healthPill'); pill.className = 'pill ' + (hp >= 80 ? 'ok' : hp >= 50 ? 'warn' : 'err'); $('#healthTxt').textContent = `LINK ${hp}% healthy`; } function renderStats(s){ $('#sTotal').textContent = fmtInt(s.total); $('#sConnected').textContent = fmtInt(s.connected); $('#sPending').textContent = fmtInt((s.pending||0)+(s.warning||0)); $('#sIssues').textContent = fmtInt((s.broken||0)+(s.errorsToday||0)); $('#sReq').textContent = fmtInt(s.requestsToday); $('#sHealth').textContent = (s.healthPct ?? 0) + '%'; } function renderCategories(cats){ const wrap = $('#catlist'); // Keep first "All" row; rebuild the rest wrap.querySelectorAll('.cat:not([data-cat="__all__"])').forEach(n=>n.remove()); const total = Object.keys(state.providers).length; $('#catAll').textContent = total; cats.forEach(c => { const row = document.createElement('div'); row.className = 'cat'; row.dataset.cat = c.name; row.innerHTML = `<span>${esc(c.name)}</span><span class="n">${c.connected}/${c.count}</span>`; row.addEventListener('click', () => { setCategory(c.name); }); wrap.appendChild(row); }); $$('#catlist .cat[data-cat="__all__"]').forEach(n => n.addEventListener('click', () => setCategory('__all__'))); syncCategoryActive(); } function syncCategoryActive(){ $$('#catlist .cat').forEach(n => n.classList.toggle('active', n.dataset.cat === state.filter.category)); } function setCategory(cat){ state.filter.category = cat; syncCategoryActive(); renderGrid(); } function renderGrid(){ const grid = $('#grid'); const q = state.filter.q.trim().toLowerCase(); const providers = Object.entries(state.providers).filter(([key, p]) => { if (state.filter.status !== '__all__' && p.status !== state.filter.status) return false; if (state.filter.category !== '__all__' && p.category !== state.filter.category) return false; if (q) { const hay = [p.name, p.category, p.description, (p.scopes||[]).join(' '), (p.mappedFiles||[]).join(' ')] .join(' ').toLowerCase(); if (!hay.includes(q)) return false; } return true; }); if (providers.length === 0){ grid.innerHTML = `<div class="empty">No providers match the current filter.</div>`; return; } grid.innerHTML = providers.map(([key, p]) => ` <article class="prov ${state.selected===key?'selected':''}" data-key="${esc(key)}" data-status="${esc(p.status||'pending')}"> <div class="tier">${esc(p.tier||'')}</div> <div class="head"> <div class="icon">${esc((p.icon||p.name||'·').slice(0,4))}</div> <div> <div class="name">${esc(p.name||key)}</div> <div class="cat">${esc(p.category||'')}</div> </div> </div> <div class="desc">${esc(p.description||'')}</div> <div class="statusRow"> <span class="status-dot"></span> <span>${esc(p.connection||p.status||'')}</span> </div> <div class="meta"> <span>sync: ${esc(p.lastSync||'—')}</span> <span>${fmtInt(p.requestsToday)} req · ${fmtInt(p.errorsToday)} err</span> </div> </article> `).join(''); $$('#grid .prov').forEach(n => n.addEventListener('click', () => selectProvider(n.dataset.key))); // Default selection if (!state.selected) { const pre = Object.entries(state.providers).find(([,p]) => p.selected) ?? providers[0]; if (pre) selectProvider(pre[0]); } } function selectProvider(key){ state.selected = key; $$('#grid .prov').forEach(n => n.classList.toggle('selected', n.dataset.key === key)); const p = state.providers[key]; if (!p) return; $('#detailEmpty').style.display = 'none'; $('#detailBody').style.display = ''; $('#detailTitle').textContent = (p.name || key) + ' — Detail'; $('#dName').textContent = p.name || key; $('#dCat').textContent = p.category || '—'; $('#dTier').textContent = p.tier || '—'; $('#dEnv').textContent = p.environment || '—'; $('#dStatus').textContent = p.status || '—'; $('#dConn').textContent = p.connection || '—'; $('#dAuth').textContent = (p.authType || '—') + ' · ' + (p.authHeader || ''); $('#dCred').textContent = p.credentialRef || '—'; $('#dSync').textContent = p.lastSync || '—'; $('#dHand').textContent = p.handshake || '—'; $('#dRt').textContent = p.responseTime || '—'; $('#dToday').textContent = `${fmtInt(p.requestsToday)} req / ${fmtInt(p.errorsToday)} err`; const score = Number(p.validationScore || 0); $('#dScore').textContent = score + '%'; $('#dBar').style.width = Math.max(0, Math.min(100, score)) + '%'; fillList('#dScopes', p.scopes); fillList('#dFiles', p.mappedFiles); fillList('#dDeps', p.dependencies); } function fillList(sel, arr){ const el = $(sel); const items = Array.isArray(arr) ? arr : []; el.innerHTML = items.length ? items.map(s => `<li>${esc(s)}</li>`).join('') : '<li style="color:var(--muted)">—</li>'; } /* ---------- Events ---------- */ $$('#filters .chip').forEach(n => n.addEventListener('click', () => { state.filter.status = n.dataset.status; $$('#filters .chip').forEach(x => x.classList.toggle('active', x===n)); renderGrid(); })); $('#q').addEventListener('input', e => { state.filter.q = e.target.value; renderGrid(); }); $('#dTestBtn').addEventListener('click', async () => { if (!state.selected) return; const p = state.providers[state.selected]; if (!p?.carrierKey) return alert('No carrier mapping for this provider.'); const url = `${CONN_BASE}/connector.php?carrier=${encodeURIComponent(p.carrierKey)}`; const t0 = performance.now(); try{ const r = await fetch(url, {cache:'no-store'}); const j = await r.json().catch(()=>({})); const dt = Math.round(performance.now() - t0); alert(`${p.name}\nHTTP ${r.status} · ${dt}ms\nitems: ${j.itemCount ?? '—'}${j.error?'\nerror: '+j.error:''}`); }catch(err){ alert(`${p.name}\nrequest failed: ${err.message}`); } }); $('#dRegistryBtn').addEventListener('click', () => { const p = state.providers[state.selected]; window.open(`${CONN_BASE}/status.php`, '_blank'); }); $('#dLogsBtn').addEventListener('click', () => { window.open(`${CONN_BASE}/../logs/carrier_events.ndjson`, '_blank'); }); /* ---------- Fallback snapshot (used if fetch fails) ---------- */ const FALLBACK = { checkedAt: new Date().toISOString(), stats: { total: 8, connected: 4, pending: 1, warning: 1, broken: 1, planned: 1, requestsToday: 0, errorsToday: 0, healthPct: 50 }, categories: [ {name:'AI Engines',count:1,connected:1}, {name:'Hosting & DNS',count:1,connected:0}, {name:'Dev & Source',count:1,connected:1}, {name:'Social & Messaging',count:1,connected:0}, {name:'Productivity & Maps',count:1,connected:0}, {name:'Billing & Payments',count:1,connected:1}, {name:'Edge & Security',count:1,connected:1}, {name:'Publishing & Media',count:1,connected:0} ], providers: {} // offline: empty }; /* ---------- Boot ---------- */ load(); </script> </body> </html>
Save file
Quick jump
open a path
Open