111 lines
5.7 KiB
JavaScript
111 lines
5.7 KiB
JavaScript
// =============================================================
|
|
// WEVAL ARCHI SPOTLIGHT V83 · Quick jumper · Ctrl+K / Cmd+K
|
|
// Cherche dans 280 pages / 730 APIs / 4 CRMs / 906 agents
|
|
// =============================================================
|
|
(function(){
|
|
if (window.__WEVAL_SPOTLIGHT_LOADED) return;
|
|
window.__WEVAL_SPOTLIGHT_LOADED = true;
|
|
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
#wevsp-backdrop {
|
|
position: fixed; inset: 0; z-index: 99997;
|
|
background: rgba(15,23,42,.65);
|
|
backdrop-filter: blur(8px);
|
|
display: none; align-items: flex-start; justify-content: center; padding-top: 12vh;
|
|
}
|
|
#wevsp-backdrop.open { display: flex; animation: wevsp-fade .15s; }
|
|
@keyframes wevsp-fade { from{opacity:0} to{opacity:1} }
|
|
#wevsp-box {
|
|
width: min(640px, 92vw); background: rgba(15,23,42,.98);
|
|
border-radius: 14px; border: 1px solid rgba(148,163,184,.25);
|
|
box-shadow: 0 24px 64px rgba(0,0,0,.6);
|
|
overflow: hidden; font: 400 14px/1.4 system-ui, sans-serif;
|
|
}
|
|
#wevsp-input { width:100%; padding: 16px 20px; background: transparent;
|
|
border: 0; border-bottom: 1px solid rgba(148,163,184,.15);
|
|
color: #e2e8f0; font-size: 16px; outline: none; font-weight: 500;
|
|
}
|
|
#wevsp-input::placeholder { color: #64748b; }
|
|
#wevsp-results { max-height: 55vh; overflow-y: auto; padding: 6px 0; }
|
|
.wevsp-row { display:flex; align-items:center; gap:12px; padding: 10px 20px; cursor: pointer; color:#cbd5e1; }
|
|
.wevsp-row:hover, .wevsp-row.active { background: rgba(20,184,166,0.10); color: #5eead4; }
|
|
.wevsp-row .emo { font-size: 18px; width: 22px; text-align:center; }
|
|
.wevsp-row .name { font-weight: 600; flex: 1; }
|
|
.wevsp-row .role { font-size: 11px; color: #94a3b8; margin-top: 2px; }
|
|
.wevsp-row .cat { font-size: 10px; padding: 2px 8px; border-radius: 10px; background: rgba(148,163,184,0.12); color: #94a3b8; text-transform: uppercase; letter-spacing:.5px; }
|
|
#wevsp-foot { padding: 10px 20px; font-size: 11px; color: #64748b; border-top: 1px solid rgba(148,163,184,.12); display:flex; justify-content: space-between;}
|
|
#wevsp-foot kbd { background: rgba(148,163,184,.15); padding: 2px 6px; border-radius: 4px; font-family: inherit; }
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
const wrap = document.createElement('div');
|
|
wrap.id = 'wevsp-backdrop';
|
|
wrap.innerHTML = `<div id="wevsp-box">
|
|
<input id="wevsp-input" placeholder="🔍 Spotlight · cherche dashboard, CRM, doctrine, doc…" autocomplete="off" />
|
|
<div id="wevsp-results"></div>
|
|
<div id="wevsp-foot"><span>↑↓ naviguer · Enter ouvrir · Esc fermer</span><span><kbd>Ctrl</kbd>+<kbd>K</kbd> pour ouvrir</span></div>
|
|
</div>`;
|
|
document.body.appendChild(wrap);
|
|
|
|
let items = [];
|
|
let filtered = [];
|
|
let activeIdx = 0;
|
|
|
|
async function load() {
|
|
try {
|
|
const r = await fetch('/api/weval-archi-manifest.php?t='+Date.now(), {cache:'no-store'});
|
|
const d = await r.json();
|
|
items = [];
|
|
const emojis = {canonical:'⭐', business:'💼', crm:'🔗', ia:'🧠', infra:'🏗️', pharma:'⚕️', email:'📧'};
|
|
Object.entries(d.dashboards).forEach(([cat, list]) => {
|
|
list.forEach(x => items.push({name: x.name, url: x.url, role: x.role||'', cat, emo: emojis[cat]||'📄'}));
|
|
});
|
|
// Add servers as searchable
|
|
(d.servers||[]).forEach(s => items.push({name: s.name, url: '#', role: s.role + ' · ' + s.ip, cat: 'infra', emo: '🖥️'}));
|
|
// Add CRMs
|
|
(d.crms||[]).forEach(c => items.push({name: c.name, url: c.url, role: c.role||'', cat: 'crm', emo: '🔗'}));
|
|
} catch(e) { console.error('[spotlight]', e); }
|
|
}
|
|
|
|
function render(query) {
|
|
const q = (query||'').trim().toLowerCase();
|
|
filtered = q ? items.filter(x => (x.name+' '+x.role+' '+x.cat).toLowerCase().includes(q)) : items;
|
|
activeIdx = 0;
|
|
const html = filtered.slice(0, 30).map((x, i) =>
|
|
`<div class="wevsp-row${i===activeIdx?' active':''}" data-idx="${i}">
|
|
<span class="emo">${x.emo}</span>
|
|
<div style="flex:1"><div class="name">${x.name}</div>${x.role?`<div class="role">${x.role}</div>`:''}</div>
|
|
<span class="cat">${x.cat}</span>
|
|
</div>`
|
|
).join('');
|
|
document.getElementById('wevsp-results').innerHTML = html || '<div style="padding:30px;text-align:center;color:#64748b">Aucun résultat</div>';
|
|
}
|
|
|
|
function open() { wrap.classList.add('open'); document.getElementById('wevsp-input').focus(); document.getElementById('wevsp-input').value=''; render(''); }
|
|
function close() { wrap.classList.remove('open'); }
|
|
function navigate(idx) { if (!filtered[idx]) return; if (filtered[idx].url && filtered[idx].url !== '#') window.open(filtered[idx].url, '_blank'); close(); }
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
|
|
e.preventDefault();
|
|
open();
|
|
return;
|
|
}
|
|
if (!wrap.classList.contains('open')) return;
|
|
if (e.key === 'Escape') close();
|
|
if (e.key === 'ArrowDown') { activeIdx = Math.min(activeIdx+1, filtered.length-1); render(document.getElementById('wevsp-input').value); e.preventDefault(); }
|
|
if (e.key === 'ArrowUp') { activeIdx = Math.max(0, activeIdx-1); render(document.getElementById('wevsp-input').value); e.preventDefault(); }
|
|
if (e.key === 'Enter') { navigate(activeIdx); e.preventDefault(); }
|
|
});
|
|
|
|
wrap.addEventListener('click', (e) => { if (e.target === wrap) close(); });
|
|
document.getElementById('wevsp-input').addEventListener('input', (e) => render(e.target.value));
|
|
document.getElementById('wevsp-results').addEventListener('click', (e) => {
|
|
const row = e.target.closest('.wevsp-row');
|
|
if (row) navigate(parseInt(row.dataset.idx));
|
|
});
|
|
|
|
load();
|
|
})();
|