/** * Minecraft Server Status - Sidebar Widget * Source : API Crafty Controller (proxy PHP crafty-stats.php) */ (function () { 'use strict'; const API = '/data/api/crafty-stats.php'; const MODS_API = '/data/api/mods-mc.php'; const INTERVAL = 30000; // 30s const SERVERS = [ { key: 'mc1', cardId: 'mc-card-0' }, { key: 'mc2', cardId: 'mc-card-1' }, ]; let timer = null; let modsData = null; // cache des mods, chargé une seule fois // ── Mods ──────────────────────────────────────────────────────────────── const modsStyle = document.createElement('style'); modsStyle.textContent = ` .mc-mods-section { margin-top: 0.5rem; border-top: 1px solid rgba(98, 114, 164, 0.3); padding-top: 0.5rem; } .mc-mods-toggle { display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding: 0.25rem 0; user-select: none; } .mc-mods-toggle-label { font-size: 0.72rem; font-weight: 700; color: #6272a4; text-transform: uppercase; letter-spacing: 0.07em; } .mc-mods-toggle-meta { display: flex; align-items: center; gap: 0.4rem; font-size: 0.68rem; color: #6272a4; } .mc-mods-arrow { font-size: 0.6rem; color: #6272a4; transition: transform 0.2s; display: inline-block; } .mc-mods-arrow.open { transform: rotate(90deg); } .mc-mods-pills { display: none; flex-wrap: wrap; gap: 0.3rem; padding-top: 0.45rem; } .mc-mods-pills.open { display: flex; } .mc-mod-pill { background: rgba(98, 114, 164, 0.25); color: #f8f8f2; font-size: 0.65rem; padding: 2px 7px; border-radius: 20px; border: 1px solid rgba(98, 114, 164, 0.35); white-space: nowrap; cursor: default; } .mc-mod-pill:hover { background: rgba(189, 147, 249, 0.2); border-color: rgba(189, 147, 249, 0.5); color: #bd93f9; } .mc-mods-error { font-size: 0.7rem; color: #ff5555; padding-top: 0.3rem; font-style: italic; } `; document.head.appendChild(modsStyle); async function fetchMods() { try { const res = await fetch(MODS_API, { cache: 'no-cache' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); modsData = await res.json(); } catch (e) { console.warn('[mods-mc] Erreur fetch :', e); } } function appendMods(card, srv) { if (!modsData) return; // trouve l'entrée correspondant au nom du serveur Crafty const entry = modsData.find(m => m.label === srv?.name) ?? modsData[SERVERS.findIndex(s => s.cardId === card.id)]; if (!entry) return; const section = document.createElement('div'); section.className = 'mc-mods-section'; if (entry.error) { const err = document.createElement('div'); err.className = 'mc-mods-error'; err.textContent = entry.error; section.appendChild(err); card.appendChild(section); return; } const count = entry.mods.length; const toggle = document.createElement('div'); toggle.className = 'mc-mods-toggle'; toggle.innerHTML = ` Mods `; const pills = document.createElement('div'); pills.className = 'mc-mods-pills'; entry.mods.forEach(mod => { const pill = document.createElement('span'); pill.className = 'mc-mod-pill'; pill.title = mod.file; pill.textContent = mod.name; pills.appendChild(pill); }); toggle.addEventListener('click', () => { pills.classList.toggle('open'); toggle.querySelector('.mc-mods-arrow').classList.toggle('open'); }); section.appendChild(toggle); section.appendChild(pills); card.appendChild(section); } // ── Status ─────────────────────────────────────────────────────────────── async function fetchAll() { let data; try { const res = await fetch(API, { cache: 'no-cache' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); data = await res.json(); } catch (e) { console.error('crafty-stats error:', e); return; } if (!data.success) return; SERVERS.forEach(({ key, cardId }) => { const card = document.getElementById(cardId); if (!card) return; renderCard(card, data.servers?.[key]); }); updateStatCards(data.servers); } function renderCard(card, srv) { if (!srv) { card.innerHTML = `