Files
wevia-brain/s89-arsenal-screens/wevia-nexus-ultimate-2026.html
2026-04-12 23:01:36 +02:00

916 lines
47 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVIA NEXUS ULTIMATE 2026 — SINGULARITY</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Outfit:wght@200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0a0f; --surface: #111118; --surface2: #16161f; --surface3: #1c1c28;
--border: #252535; --border2: #2f2f42;
--text: #e8e8f0; --text2: #9999b3; --text3: #666680;
--primary: #00ffaa; --primary2: #00cc88; --primary-glow: rgba(0,255,170,0.15);
--secondary: #7b68ee; --secondary-glow: rgba(123,104,238,0.15);
--accent: #ff6b6b; --warning: #ffd93d; --info: #4ecdc4;
--success: #00ffaa; --danger: #ff4757;
--gradient1: linear-gradient(135deg, #00ffaa 0%, #7b68ee 50%, #ff6b6b 100%);
--gradient2: linear-gradient(135deg, #0a0a0f 0%, #111125 100%);
--font: 'Outfit', sans-serif; --mono: 'JetBrains Mono', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { font-family: var(--font); background: var(--bg); color: var(--text); min-height: 100vh; overflow-x: hidden; }
/* ═══ GLOBAL NOISE ═══ */
body::before {
content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
pointer-events: none; z-index: 0; opacity: 0.4;
}
/* ═══ ORBS ═══ */
.orb { position: fixed; border-radius: 50%; filter: blur(100px); pointer-events: none; z-index: 0; animation: orbFloat 20s infinite ease-in-out; }
.orb1 { width: 600px; height: 600px; background: rgba(0,255,170,0.06); top: -200px; right: -200px; }
.orb2 { width: 500px; height: 500px; background: rgba(123,104,238,0.06); bottom: -150px; left: -150px; animation-delay: -10s; }
.orb3 { width: 400px; height: 400px; background: rgba(255,107,107,0.04); top: 50%; left: 50%; animation-delay: -5s; }
@keyframes orbFloat { 0%,100% { transform: translate(0,0) scale(1); } 33% { transform: translate(30px,-20px) scale(1.05); } 66% { transform: translate(-20px,30px) scale(0.95); } }
/* ═══ HEADER ═══ */
.header {
position: sticky; top: 0; z-index: 100; background: rgba(10,10,15,0.85);
backdrop-filter: blur(20px); border-bottom: 1px solid var(--border);
padding: 0 24px; height: 64px; display: flex; align-items: center; justify-content: space-between;
}
.logo { display: flex; align-items: center; gap: 12px; }
.logo-icon {
width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center;
background: var(--gradient1); font-size: 20px; position: relative; overflow: hidden;
}
.logo-icon::after { content: ''; position: absolute; inset: 2px; border-radius: 8px; background: var(--bg); }
.logo-icon span { position: relative; z-index: 1; }
.logo-text h1 { font-size: 16px; font-weight: 700; letter-spacing: 2px; background: var(--gradient1); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.logo-text p { font-size: 10px; color: var(--text3); font-family: var(--mono); letter-spacing: 3px; text-transform: uppercase; }
.header-status { display: flex; align-items: center; gap: 16px; }
.status-badge {
display: flex; align-items: center; gap: 6px; padding: 6px 14px;
background: var(--primary-glow); border: 1px solid rgba(0,255,170,0.3);
border-radius: 20px; font-family: var(--mono); font-size: 11px; color: var(--primary);
}
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--primary); animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { opacity: 1; box-shadow: 0 0 0 0 rgba(0,255,170,0.4); } 50% { opacity: 0.8; box-shadow: 0 0 0 6px rgba(0,255,170,0); } }
.header-actions { display: flex; gap: 8px; }
.header-btn {
background: var(--surface2); border: 1px solid var(--border); color: var(--text2);
padding: 6px 12px; border-radius: 8px; cursor: pointer; font-family: var(--mono);
font-size: 11px; transition: all 0.2s; display: flex; align-items: center; gap: 6px;
}
.header-btn:hover { background: var(--surface3); border-color: var(--primary); color: var(--primary); }
/* ═══ MAIN LAYOUT ═══ */
.main { position: relative; z-index: 1; max-width: 1600px; margin: 0 auto; padding: 24px; }
/* ═══ HERO SECTION ═══ */
.hero {
position: relative; padding: 48px 40px; margin-bottom: 24px;
background: var(--gradient2); border: 1px solid var(--border);
border-radius: 16px; overflow: hidden;
}
.hero::before {
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
background: var(--gradient1);
}
.hero-grid { display: grid; grid-template-columns: 1fr auto; gap: 40px; align-items: center; }
.hero h2 { font-size: 32px; font-weight: 800; line-height: 1.1; margin-bottom: 8px; }
.hero h2 span { background: var(--gradient1); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.hero .subtitle { font-size: 14px; color: var(--text2); margin-bottom: 20px; font-family: var(--mono); }
.hero-stats { display: flex; gap: 32px; }
.hero-stat { text-align: center; }
.hero-stat .val { font-size: 28px; font-weight: 800; color: var(--primary); font-family: var(--mono); }
.hero-stat .lbl { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: 1px; margin-top: 2px; }
.hero-right { display: flex; flex-direction: column; gap: 12px; }
.go-live-btn {
background: var(--gradient1); color: #000; border: none; padding: 16px 40px;
border-radius: 12px; font-size: 16px; font-weight: 800; letter-spacing: 2px;
cursor: pointer; font-family: var(--font); transition: all 0.3s;
text-transform: uppercase; position: relative; overflow: hidden;
}
.go-live-btn::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.2) 50%, transparent 100%); transform: translateX(-100%); animation: shimmer 3s infinite; }
@keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }
.go-live-btn:hover { transform: scale(1.02); box-shadow: 0 0 40px rgba(0,255,170,0.3); }
/* ═══ GRID LAYOUT ═══ */
.grid { display: grid; gap: 16px; margin-bottom: 24px; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: 1fr 1fr; }
.grid-12 { grid-template-columns: repeat(12, 1fr); }
/* ═══ CARDS ═══ */
.card {
background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
padding: 20px; position: relative; overflow: hidden; transition: all 0.3s;
}
.card:hover { border-color: var(--border2); transform: translateY(-1px); }
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.card-title { font-size: 12px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--text3); font-weight: 600; display: flex; align-items: center; gap: 8px; }
.card-title i { color: var(--primary); }
.card-badge { font-size: 10px; padding: 3px 8px; border-radius: 10px; font-family: var(--mono); font-weight: 600; }
.badge-live { background: rgba(0,255,170,0.15); color: var(--primary); border: 1px solid rgba(0,255,170,0.3); }
.badge-warn { background: rgba(255,217,61,0.15); color: var(--warning); border: 1px solid rgba(255,217,61,0.3); }
.badge-down { background: rgba(255,71,87,0.15); color: var(--danger); border: 1px solid rgba(255,71,87,0.3); }
.metric { margin-bottom: 12px; }
.metric-val { font-size: 32px; font-weight: 800; font-family: var(--mono); line-height: 1; }
.metric-label { font-size: 11px; color: var(--text3); margin-top: 4px; }
/* ═══ MODULES GRID ═══ */
.modules-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
.module-card {
background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
padding: 14px; cursor: pointer; transition: all 0.2s; position: relative;
}
.module-card:hover { border-color: var(--primary); background: var(--surface3); }
.module-card.active::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: var(--primary); border-radius: 10px 10px 0 0; }
.module-icon { font-size: 24px; margin-bottom: 8px; }
.module-name { font-size: 12px; font-weight: 600; margin-bottom: 2px; }
.module-desc { font-size: 10px; color: var(--text3); font-family: var(--mono); }
.module-status { position: absolute; top: 10px; right: 10px; width: 8px; height: 8px; border-radius: 50%; }
.module-status.on { background: var(--primary); box-shadow: 0 0 8px rgba(0,255,170,0.5); }
.module-status.off { background: var(--danger); }
/* ═══ PROVIDERS ═══ */
.provider-row {
display: flex; align-items: center; gap: 12px; padding: 10px 12px;
background: var(--surface2); border-radius: 8px; margin-bottom: 6px;
border: 1px solid transparent; transition: all 0.2s;
}
.provider-row:hover { border-color: var(--border2); }
.provider-icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; }
.provider-info { flex: 1; }
.provider-name { font-size: 13px; font-weight: 600; }
.provider-model { font-size: 10px; color: var(--text3); font-family: var(--mono); }
.provider-status { font-size: 10px; font-family: var(--mono); padding: 3px 8px; border-radius: 6px; }
/* ═══ CHAT PANEL ═══ */
.chat-panel {
background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
display: flex; flex-direction: column; height: 500px;
}
.chat-header {
padding: 14px 20px; border-bottom: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between;
}
.chat-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.chat-msg { max-width: 85%; padding: 12px 16px; border-radius: 12px; font-size: 13px; line-height: 1.5; animation: fadeIn 0.3s; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.msg-user { background: var(--secondary-glow); border: 1px solid rgba(123,104,238,0.3); align-self: flex-end; }
.msg-ai { background: var(--surface2); border: 1px solid var(--border); align-self: flex-start; }
.msg-meta { font-size: 9px; color: var(--text3); font-family: var(--mono); margin-top: 6px; }
.chat-input-area { padding: 12px 16px; border-top: 1px solid var(--border); display: flex; gap: 8px; }
.chat-input {
flex: 1; background: var(--surface2); border: 1px solid var(--border); color: var(--text);
padding: 10px 14px; border-radius: 10px; font-family: var(--font); font-size: 13px;
outline: none; resize: none;
}
.chat-input:focus { border-color: var(--primary); }
.chat-send {
background: var(--primary); color: #000; border: none; width: 40px; height: 40px;
border-radius: 10px; cursor: pointer; font-size: 16px; transition: all 0.2s;
}
.chat-send:hover { transform: scale(1.05); background: var(--primary2); }
/* ═══ DARK MODULE ═══ */
.dark-panel { background: linear-gradient(135deg, #0f0f18, #151525); }
.dark-input {
width: 100%; background: rgba(0,0,0,0.3); border: 1px solid var(--border);
color: var(--primary); padding: 10px 14px; border-radius: 8px;
font-family: var(--mono); font-size: 12px; outline: none;
}
.dark-results { margin-top: 12px; max-height: 300px; overflow-y: auto; }
.dark-item { padding: 8px; border-bottom: 1px solid var(--border); font-family: var(--mono); font-size: 11px; }
/* ═══ SOCIAL ═══ */
.social-platforms { display: flex; gap: 8px; margin-bottom: 12px; }
.social-btn {
padding: 6px 14px; border-radius: 8px; border: 1px solid var(--border);
background: var(--surface2); color: var(--text2); cursor: pointer; font-size: 12px;
transition: all 0.2s;
}
.social-btn:hover, .social-btn.active { border-color: var(--secondary); color: var(--secondary); background: var(--secondary-glow); }
.social-output { background: var(--surface2); border-radius: 8px; padding: 14px; font-size: 12px; line-height: 1.6; min-height: 120px; white-space: pre-wrap; }
/* ═══ SERVERS ═══ */
.server-row {
display: flex; align-items: center; gap: 12px; padding: 8px 12px;
border-bottom: 1px solid var(--border); font-family: var(--mono); font-size: 11px;
}
.server-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.server-name { flex: 1; font-weight: 500; color: var(--text); }
.server-ip { color: var(--text3); }
.server-role { color: var(--text2); font-size: 10px; }
/* ═══ TERMINAL ═══ */
.terminal {
background: #000; border-radius: 10px; padding: 16px; font-family: var(--mono);
font-size: 11px; color: var(--primary); max-height: 250px; overflow-y: auto;
line-height: 1.6;
}
.terminal .cmd { color: var(--text3); }
.terminal .ok { color: var(--success); }
.terminal .err { color: var(--danger); }
.terminal .warn { color: var(--warning); }
.terminal .info { color: var(--info); }
/* ═══ PROGRESS ═══ */
.progress-bar { height: 6px; background: var(--surface3); border-radius: 3px; overflow: hidden; margin-top: 8px; }
.progress-fill { height: 100%; border-radius: 3px; transition: width 1s ease; }
.progress-fill.green { background: var(--gradient1); }
.progress-fill.yellow { background: linear-gradient(90deg, var(--warning), #ff9f43); }
.progress-fill.red { background: linear-gradient(90deg, var(--danger), #ff6b81); }
/* ═══ TABS ═══ */
.tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--border); padding-bottom: 8px; }
.tab {
padding: 6px 14px; border-radius: 8px 8px 0 0; cursor: pointer; font-size: 12px;
color: var(--text3); transition: all 0.2s; border: 1px solid transparent; border-bottom: none;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--primary); background: var(--surface2); border-color: var(--border); }
/* ═══ SCROLL ═══ */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--surface); }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 2px; }
/* ═══ RESPONSIVE ═══ */
@media (max-width: 1200px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 768px) {
.grid-3, .grid-2, .grid-4 { grid-template-columns: 1fr; }
.hero-grid { grid-template-columns: 1fr; }
.hero-stats { flex-wrap: wrap; gap: 16px; }
.header { padding: 0 12px; }
.main { padding: 12px; }
.modules-grid { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div class="orb orb1"></div>
<div class="orb orb2"></div>
<div class="orb orb3"></div>
<!-- ═══ HEADER ═══ -->
<header class="header">
<div class="logo">
<div class="logo-icon"><span>🧠</span></div>
<div class="logo-text">
<h1>WEVIA NEXUS</h1>
<p>SINGULARITY · 2026</p>
</div>
</div>
<div class="header-status">
<div class="status-badge"><div class="status-dot"></div><span id="statusText">INITIALIZING...</span></div>
<div class="header-actions">
<button class="header-btn" onclick="refreshAll()"><i class="fas fa-sync-alt"></i> Refresh</button>
<button class="header-btn" onclick="testProviders()"><i class="fas fa-vial"></i> Test IA</button>
<button class="header-btn" onclick="showArchitecture()"><i class="fas fa-project-diagram"></i> Archi</button>
</div>
</div>
</header>
<!-- ═══ MAIN ═══ -->
<div class="main">
<!-- HERO -->
<div class="hero" id="hero">
<div class="hero-grid">
<div>
<h2>GO LIVE — <span>SINGULARITY</span></h2>
<p class="subtitle">L'IA LA PLUS COMPLÈTE DE 2026 · TOUS MODULES CONNECTÉS</p>
<div class="hero-stats">
<div class="hero-stat"><div class="val" id="hModules"></div><div class="lbl">Modules</div></div>
<div class="hero-stat"><div class="val" id="hProviders"></div><div class="lbl">IA Providers</div></div>
<div class="hero-stat"><div class="val" id="hServers"></div><div class="lbl">Serveurs</div></div>
<div class="hero-stat"><div class="val" id="hKB"></div><div class="lbl">KB Entries</div></div>
<div class="hero-stat"><div class="val" id="hGPU"></div><div class="lbl">GPU Models</div></div>
<div class="hero-stat"><div class="val" id="hCrons"></div><div class="lbl">Crons</div></div>
</div>
</div>
<div class="hero-right">
<button class="go-live-btn" onclick="goLive()">⚡ GO LIVE 2026</button>
<div style="font-size:10px;color:var(--text3);text-align:center;font-family:var(--mono)">WEVAL Consulting · Casablanca</div>
</div>
</div>
</div>
<!-- ═══ HEALTH METRICS ═══ -->
<div class="grid grid-4" id="metricsGrid">
<div class="card">
<div class="card-header"><span class="card-title"><i class="fas fa-microchip"></i> CPU / LOAD</span><span class="card-badge badge-live" id="cpuBadge">LIVE</span></div>
<div class="metric"><div class="metric-val" id="cpuLoad"></div><div class="metric-label">Load Average</div></div>
<div class="progress-bar"><div class="progress-fill green" id="cpuBar" style="width:0%"></div></div>
</div>
<div class="card">
<div class="card-header"><span class="card-title"><i class="fas fa-memory"></i> MÉMOIRE</span><span class="card-badge badge-live">RAM</span></div>
<div class="metric"><div class="metric-val" id="memVal"></div><div class="metric-label" id="memLabel">Usage</div></div>
<div class="progress-bar"><div class="progress-fill green" id="memBar" style="width:0%"></div></div>
</div>
<div class="card">
<div class="card-header"><span class="card-title"><i class="fas fa-hdd"></i> DISQUE</span><span class="card-badge badge-live">SSD</span></div>
<div class="metric"><div class="metric-val" id="diskVal"></div><div class="metric-label" id="diskLabel">Espace</div></div>
<div class="progress-bar"><div class="progress-fill yellow" id="diskBar" style="width:0%"></div></div>
</div>
<div class="card">
<div class="card-header"><span class="card-title"><i class="fas fa-clock"></i> UPTIME</span><span class="card-badge badge-live">ON</span></div>
<div class="metric"><div class="metric-val" id="uptimeVal" style="font-size:18px"></div><div class="metric-label">Serveur principal</div></div>
</div>
</div>
<!-- ═══ MODULES CONNECTÉS ═══ -->
<div class="card" style="margin-bottom:24px">
<div class="card-header">
<span class="card-title"><i class="fas fa-cube"></i> MODULES CONNECTÉS — ARCHITECTURE COMPLÈTE</span>
<span class="card-badge badge-live" id="modulesCount">11 ACTIVE</span>
</div>
<div class="modules-grid" id="modulesGrid"></div>
</div>
<!-- ═══ MAIN PANELS ═══ -->
<div class="grid grid-2">
<!-- CHAT IA -->
<div class="chat-panel">
<div class="chat-header">
<span class="card-title"><i class="fas fa-robot"></i> BRAIN ENGINE — CHAT IA</span>
<select id="providerSelect" style="background:var(--surface2);border:1px solid var(--border);color:var(--text);padding:4px 8px;border-radius:6px;font-size:11px;font-family:var(--mono)">
<option value="auto">🤖 Auto (Failover)</option>
<option value="cerebras">⚡ Cerebras</option>
<option value="groq">🚀 Groq</option>
<option value="sambanova">🔥 SambaNova</option>
<option value="ollama-gpu">🖥️ Ollama GPU (DeepSeek R1 32B)</option>
<option value="ollama-qwen">💻 Ollama Qwen Coder 14B</option>
<option value="ollama-llama">🦙 Ollama Llama 3.1 8B</option>
<option value="deepseek">🌊 DeepSeek</option>
<option value="mistral">🇫🇷 Mistral</option>
</select>
</div>
<div class="chat-messages" id="chatMessages">
<div class="msg-ai">
🧠 <strong>WEVIA NEXUS ULTIMATE 2026</strong> — SINGULARITY<br><br>
Tous les modules sont connectés. Je suis l'IA la plus complète de 2026 avec :<br>
• 11 Providers IA (Cloud + GPU Local)<br>
• 7 Serveurs interconnectés<br>
• 1710+ entrées KB<br>
• Dark Intelligence + Inconscient Collectif + Réseaux Sociaux<br><br>
Que puis-je faire pour vous ?
<div class="msg-meta">WEVIA NEXUS · SINGULARITY · Prêt</div>
</div>
</div>
<div class="chat-input-area">
<textarea class="chat-input" id="chatInput" rows="1" placeholder="Message à WEVIA NEXUS..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat()}"></textarea>
<button class="chat-send" onclick="sendChat()"><i class="fas fa-arrow-up"></i></button>
</div>
</div>
<!-- RIGHT COLUMN -->
<div style="display:flex;flex-direction:column;gap:16px">
<!-- PROVIDERS -->
<div class="card" style="flex:1">
<div class="card-header">
<span class="card-title"><i class="fas fa-brain"></i> IA PROVIDERS</span>
<button class="header-btn" onclick="testProviders()" style="font-size:10px;padding:4px 10px"><i class="fas fa-play"></i> Test All</button>
</div>
<div id="providersList"></div>
</div>
<!-- SERVERS -->
<div class="card">
<div class="card-header">
<span class="card-title"><i class="fas fa-server"></i> SERVEURS</span>
<span class="card-badge badge-live" id="serversCount">7</span>
</div>
<div id="serversList"></div>
</div>
</div>
</div>
<!-- ═══ DARK + SOCIAL + COLLECTIVE ═══ -->
<div class="grid grid-3" style="margin-top:24px">
<!-- DARK INTELLIGENCE -->
<div class="card dark-panel">
<div class="card-header">
<span class="card-title"><i class="fas fa-user-secret"></i> DARK INTELLIGENCE</span>
<span class="card-badge badge-live">OSINT</span>
</div>
<input type="text" class="dark-input" id="darkTarget" placeholder="Domaine ou URL cible..." onkeydown="if(event.key==='Enter')darkScan()">
<div style="display:flex;gap:6px;margin-top:8px">
<button class="header-btn" onclick="darkScan()" style="flex:1"><i class="fas fa-search"></i> Scan DNS</button>
<button class="header-btn" onclick="darkScrape()" style="flex:1"><i class="fas fa-spider"></i> Deep Scrape</button>
</div>
<div class="dark-results" id="darkResults"></div>
</div>
<!-- INCONSCIENT COLLECTIF -->
<div class="card">
<div class="card-header">
<span class="card-title"><i class="fas fa-atom"></i> INCONSCIENT COLLECTIF</span>
<span class="card-badge badge-live">RAG+KB</span>
</div>
<input type="text" class="dark-input" id="collectiveQuery" placeholder="Interroger la mémoire collective..." style="color:var(--secondary);border-color:rgba(123,104,238,0.3)" onkeydown="if(event.key==='Enter')queryCollective()">
<button class="header-btn" onclick="queryCollective()" style="width:100%;margin-top:8px;justify-content:center"><i class="fas fa-brain"></i> Requête Collective</button>
<div id="collectiveResults" style="margin-top:12px;max-height:280px;overflow-y:auto"></div>
</div>
<!-- SOCIAL NETWORKS -->
<div class="card">
<div class="card-header">
<span class="card-title"><i class="fas fa-share-alt"></i> RÉSEAUX SOCIAUX</span>
<span class="card-badge badge-live">CONTENT</span>
</div>
<div class="social-platforms">
<button class="social-btn active" onclick="selectPlatform(this,'linkedin')"><i class="fab fa-linkedin"></i> LinkedIn</button>
<button class="social-btn" onclick="selectPlatform(this,'twitter')"><i class="fab fa-twitter"></i> Twitter</button>
<button class="social-btn" onclick="selectPlatform(this,'email_b2b')"><i class="fas fa-envelope"></i> B2B</button>
</div>
<input type="text" class="dark-input" id="socialTopic" placeholder="Sujet du contenu..." style="color:var(--info)" onkeydown="if(event.key==='Enter')generateSocial()">
<button class="header-btn" onclick="generateSocial()" style="width:100%;margin-top:8px;justify-content:center"><i class="fas fa-magic"></i> Générer</button>
<div class="social-output" id="socialOutput" style="margin-top:10px">Sélectionnez une plateforme et un sujet...</div>
</div>
</div>
<!-- ═══ TERMINAL ═══ -->
<div class="card" style="margin-top:24px">
<div class="card-header">
<span class="card-title"><i class="fas fa-terminal"></i> SYSTEM LOG</span>
<span class="card-badge badge-live" id="logBadge">LIVE</span>
</div>
<div class="terminal" id="terminal"></div>
</div>
</div>
<script>
// ═══════════════════════════════════════════════════════════════
// WEVIA NEXUS ULTIMATE 2026 — FRONTEND ENGINE
// ═══════════════════════════════════════════════════════════════
const API = 'http://89.167.40.150:80/api/wevia-nexus-ultimate.php';
let currentPlatform = 'linkedin';
let systemData = null;
// ═══ TERMINAL LOG ═══
function log(msg, type = '') {
const t = document.getElementById('terminal');
const ts = new Date().toLocaleTimeString('fr-FR');
t.innerHTML += `<div><span class="cmd">[${ts}]</span> <span class="${type}">${msg}</span></div>`;
t.scrollTop = t.scrollHeight;
}
// ═══ API CALL ═══
// === WEVIA PDF AUTO-GENERATE ===
async function handlePdfResponse(respText, msgs, r) {
const pdfMatch = respText.match(/```json-pdf\n([\s\S]*?)\n```/) || respText.match(/json-pdf\n([\s\S]*?)\n```/);
if (!pdfMatch) return false;
try {
const pdfJson = JSON.parse(pdfMatch[1]);
msgs.innerHTML += '<div class="msg-ai">\u{1F4C4} <b>Generation du document PDF en cours...</b><br><span style="color:var(--muted)">\u0022' + (pdfJson.title || 'Document') + '\u0022 \u2014 ' + (pdfJson.sections?.length || 0) + ' sections</span></div>';
msgs.scrollTop = msgs.scrollHeight;
const pdfResp = await fetch('/hamid-generate.php', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({type:'pdf', title: pdfJson.title||'Document', content: pdfJson, structured: true, brand: pdfJson.brand||'weval'})
});
const pdfData = await pdfResp.json();
if (pdfData.status === 'ok') {
msgs.innerHTML += '<div class="msg-ai" style="background:rgba(16,185,129,0.08);border-left:4px solid var(--success)">\u2705 <b>PDF genere!</b><br>\u{1F4CA} ' + (pdfData.pages||'?') + ' pages \u00b7 ' + Math.round((pdfData.size||0)/1024) + ' KB<br><a href="' + pdfData.url + '" target="_blank" style="color:var(--accent);font-weight:bold;font-size:14px">\u{1F4E5} Telecharger: ' + pdfData.filename + '</a></div>';
log('PDF: ' + pdfData.filename + ' (' + pdfData.pages + 'p)', 'ok');
} else {
msgs.innerHTML += '<div class="msg-ai"><span style="color:var(--danger)">\u274c PDF Error: ' + pdfData.error + '</span></div>';
}
return true;
} catch(e) { console.error('PDF parse:', e); return false; }
}
async function api(action, params = {}) {
try {
const url = new URL(API);
url.searchParams.set('action', action);
Object.entries(params).forEach(([k, v]) => { if (typeof v === 'string') url.searchParams.set(k, v); });
const opts = { method: 'GET' };
if (params._body) {
opts.method = 'POST';
opts.headers = { 'Content-Type': 'application/json' };
opts.body = JSON.stringify(params._body);
}
const r = await fetch(url, opts);
return await r.json();
} catch (e) {
log(`API Error: ${action}${e.message}`, 'err');
return null;
}
}
// ═══ INIT ═══
async function init() {
log('🚀 WEVIA NEXUS ULTIMATE 2026 — INITIALIZING...', 'info');
log('Codename: SINGULARITY', 'info');
await refreshAll();
renderModules();
renderProviders();
renderServers();
log('✅ ALL SYSTEMS NOMINAL — SINGULARITY ACTIVE', 'ok');
document.getElementById('statusText').textContent = 'SINGULARITY ACTIVE';
}
// ═══ REFRESH ALL ═══
async function refreshAll() {
log('Fetching system status...', 'cmd');
const data = await api('status');
if (!data) { log('❌ Cannot reach NEXUS API', 'err'); return; }
systemData = data;
// Hero stats
const a = data.arsenal || {};
document.getElementById('hModules').textContent = (a.total || 0).toLocaleString();
document.getElementById('hProviders').textContent = data.providers_configured || 11;
document.getElementById('hServers').textContent = data.servers || 7;
document.getElementById('hKB').textContent = (a.kb_entries || 0).toLocaleString();
document.getElementById('hGPU').textContent = (data.health?.gpu_models?.length || 0);
document.getElementById('hCrons').textContent = data.health?.crons || 0;
// Health metrics
const h = data.health || {};
const load = h.load?.[0]?.toFixed(2) || '—';
document.getElementById('cpuLoad').textContent = load;
const loadPct = Math.min((parseFloat(load) / 4) * 100, 100);
document.getElementById('cpuBar').style.width = loadPct + '%';
const mem = h.memory || {};
document.getElementById('memVal').textContent = mem.usage_pct ? mem.usage_pct + '%' : '—';
document.getElementById('memLabel').textContent = mem.used_mb ? `${mem.used_mb}MB / ${mem.total_mb}MB` : '';
document.getElementById('memBar').style.width = (mem.usage_pct || 0) + '%';
const disk = h.disk || {};
document.getElementById('diskVal').textContent = disk.usage_pct ? disk.usage_pct + '%' : '—';
document.getElementById('diskLabel').textContent = disk.used ? `${disk.used} / ${disk.total}` : '';
document.getElementById('diskBar').style.width = (disk.usage_pct || 0) + '%';
document.getElementById('uptimeVal').textContent = h.uptime || '—';
log(`Modules: ${a.total} | KB: ${a.kb_entries} | GPU: ${h.gpu_models?.length} models | Crons: ${h.crons}`, 'ok');
// Services
const svcs = h.services || {};
Object.entries(svcs).forEach(([name, status]) => {
log(` ${status === 'running' ? '✅' : '⚠️'} ${name}: ${status}`, status === 'running' ? 'ok' : 'warn');
});
log(` ${h.gpu_server === 'online' ? '✅' : '❌'} GPU Server: ${h.gpu_server}`, h.gpu_server === 'online' ? 'ok' : 'err');
}
// ═══ RENDER MODULES ═══
function renderModules() {
const modules = [
{ id: 'dark', icon: '🕵️', name: 'Dark Intelligence', desc: 'OSINT · Scraping · Intel', active: true },
{ id: 'collective', icon: '🧬', name: 'Inconscient Collectif', desc: 'RAG · KB · Mémoire', active: true },
{ id: 'social', icon: '📡', name: 'Réseaux Sociaux', desc: 'LinkedIn · Twitter · B2B', active: true },
{ id: 'brain', icon: '🧠', name: 'Brain Engine', desc: '11 Providers · Failover', active: true },
{ id: 'arsenal', icon: '⚔️', name: 'Arsenal Ops', desc: '1415 Modules · 150 Écrans', active: true },
{ id: 'email', icon: '📧', name: 'Email Pipeline', desc: 'PMTA · O365 · E2E', active: true },
{ id: 'sentinel', icon: '🛡️', name: 'Sentinel', desc: 'Exec · Monitor · Guard', active: true },
{ id: 'vision', icon: '👁️', name: 'Vision & OCR', desc: 'Image · PDF · Scan', active: true },
{ id: 'voice', icon: '🎤', name: 'Voice & TTS', desc: 'Speech · Audio', active: true },
{ id: 'code', icon: '💻', name: 'Code Sandbox', desc: 'Execute · CLI · Debug', active: true },
{ id: 'docs', icon: '📄', name: 'Doc Generator', desc: 'PDF · Word · Excel · PPT', active: true },
];
const grid = document.getElementById('modulesGrid');
grid.innerHTML = modules.map(m => `
<div class="module-card ${m.active ? 'active' : ''}" onclick="activateModule('${m.id}')">
<div class="module-status ${m.active ? 'on' : 'off'}"></div>
<div class="module-icon">${m.icon}</div>
<div class="module-name">${m.name}</div>
<div class="module-desc">${m.desc}</div>
</div>
`).join('');
}
// ═══ RENDER PROVIDERS ═══
function renderProviders() {
const providers = [
{ name: 'Cerebras', icon: '⚡', model: 'llama-3.3-70b', speed: '429ms', tier: 'primary' },
{ name: 'Groq', icon: '🚀', model: 'llama-3.3-70b-versatile', speed: '192ms', tier: 'primary' },
{ name: 'SambaNova', icon: '🔥', model: 'Meta-Llama-3.3-70B', speed: '800ms', tier: 'primary' },
{ name: 'Ollama GPU', icon: '🖥️', model: 'deepseek-r1:32b (RTX 4000)', speed: '2000ms', tier: 'local' },
{ name: 'Qwen Coder', icon: '💻', model: 'qwen2.5-coder:14b', speed: '1500ms', tier: 'local' },
{ name: 'Llama 3.1', icon: '🦙', model: 'llama3.1:8b', speed: '1000ms', tier: 'local' },
{ name: 'DeepSeek', icon: '🌊', model: 'deepseek-chat', speed: '1200ms', tier: 'cloud' },
{ name: 'Gemini', icon: '💎', model: 'gemini-pro', speed: '600ms', tier: 'cloud' },
{ name: 'Mistral', icon: '🇫🇷', model: 'mistral-large', speed: '700ms', tier: 'cloud' },
{ name: 'Cohere', icon: '🔮', model: 'command-r-plus', speed: '900ms', tier: 'cloud' },
{ name: 'Hyperbolic', icon: '∞', model: 'Llama-3.3-70B', speed: '500ms', tier: 'cloud' },
];
const tierColors = { primary: 'var(--primary)', local: 'var(--warning)', cloud: 'var(--info)' };
const tierLabels = { primary: 'PRIMARY', local: 'LOCAL GPU', cloud: 'CLOUD' };
document.getElementById('providersList').innerHTML = providers.map(p => `
<div class="provider-row">
<div class="provider-icon" style="background:${tierColors[p.tier]}22;color:${tierColors[p.tier]}">${p.icon}</div>
<div class="provider-info">
<div class="provider-name">${p.name}</div>
<div class="provider-model">${p.model} · ${p.speed}</div>
</div>
<span class="provider-status" style="background:${tierColors[p.tier]}22;color:${tierColors[p.tier]}">${tierLabels[p.tier]}</span>
</div>
`).join('');
}
// ═══ RENDER SERVERS ═══
function renderServers() {
const servers = [
{ name: 'WEVADS Prod', ip: '89.167.40.150', role: 'Email + Brain', online: true },
{ name: 'WEVIA IA', ip: '46.62.220.135', role: 'IA + Arsenal', online: true },
{ name: 'GPU RTX 4000', ip: '88.198.4.195', role: 'Ollama + Models', online: true },
{ name: 'MTA-EU Relay', ip: '89.167.1.139', role: 'SMTP Relay', online: true },
{ name: 'OVH Tracking', ip: '151.80.235.110', role: 'Click Tracking', online: true },
{ name: 'Huawei HW1', ip: '110.238.76.155', role: 'MTA China 1', online: true },
{ name: 'Huawei HW2', ip: '122.8.135.130', role: 'MTA China 2', online: true },
];
document.getElementById('serversList').innerHTML = servers.map(s => `
<div class="server-row">
<div class="server-dot" style="background:${s.online ? 'var(--primary)' : 'var(--danger)'}"></div>
<div class="server-name">${s.name}</div>
<div class="server-ip">${s.ip}</div>
<div class="server-role">${s.role}</div>
</div>
`).join('');
}
// ═══ CHAT ═══
async function sendChat() {
const input = document.getElementById('chatInput');
const msg = input.value.trim();
if (!msg) return;
input.value = '';
const msgs = document.getElementById('chatMessages');
msgs.innerHTML += `<div class="msg-user">${msg}</div>`;
msgs.innerHTML += `<div class="msg-ai" id="typing" style="opacity:0.5">⏳ Réflexion en cours...</div>`;
msgs.scrollTop = msgs.scrollHeight;
const provider = document.getElementById('providerSelect').value;
log(`Chat → ${provider}: "${msg.substring(0, 50)}..."`, 'info');
const data = await api('chat', { _body: { action: 'chat', message: msg, provider } });
const typingEl = document.getElementById('typing');
if (typingEl) typingEl.remove();
if (data?.result?.response) {
const r = data.result;
// Try PDF generation first
const pdfHandled = await handlePdfResponse(r.response, msgs, r);
if (!pdfHandled) {
msgs.innerHTML += `
<div class="msg-ai">
${r.response.replace(/\n/g, '<br>')}
<div class="msg-meta">${r.reasoning_tag||'🧠'} <b>${r.reasoning_mode||'standard'}</b> · Depth ${r.reasoning_depth||3}/5 │ ${r.provider||'?'} · ${r.model||'?'} · ${r.latency_ms||'?'}ms</div>
</div>`;
}
log(`✅ Response from ${r.provider} (${r.latency_ms}ms)`, 'ok');
} else {
msgs.innerHTML += `<div class="msg-ai"><span style="color:var(--danger)">❌ ${data?.result?.error || 'Erreur connexion'}</span></div>`;
log(`❌ Chat error: ${data?.result?.error || 'unknown'}`, 'err');
}
msgs.scrollTop = msgs.scrollHeight;
}
// ═══ TEST PROVIDERS ═══
async function testProviders() {
log('🧪 Testing ALL IA providers...', 'info');
const data = await api('test_providers');
if (!data?.providers) { log('❌ Test failed', 'err'); return; }
Object.entries(data.providers).forEach(([name, info]) => {
const icon = info.status === 'LIVE' ? '✅' : info.status === 'NO_KEY' ? '🔑' : '❌';
const latency = info.latency ? ` (${info.latency}ms)` : '';
log(` ${icon} ${name}: ${info.status}${latency}${info.error ? ' — ' + info.error : ''}`, info.status === 'LIVE' ? 'ok' : 'warn');
});
}
// ═══ DARK INTELLIGENCE ═══
async function darkScan() {
const target = document.getElementById('darkTarget').value.trim();
if (!target) return;
log(`🕵️ Dark Scan: ${target}`, 'info');
const data = await api('dark_scan', { target });
const results = document.getElementById('darkResults');
if (data?.dark) {
const d = data.dark;
let html = '<div style="margin-top:8px">';
if (d.osint?.dns?.length) {
html += '<div class="dark-item" style="color:var(--primary)">📡 DNS Records:</div>';
d.osint.dns.forEach(r => { html += `<div class="dark-item">&nbsp;&nbsp;${r.type}: ${r.value}</div>`; });
}
if (d.osint?.server) html += `<div class="dark-item">🖥️ Server: ${d.osint.server}</div>`;
if (d.osint?.security_headers) {
const sh = d.osint.security_headers;
html += `<div class="dark-item">🔒 CSP: ${sh.csp ? '✅' : '❌'} | HSTS: ${sh.hsts ? '✅' : '❌'} | X-Frame: ${sh.xframe ? '✅' : '❌'}</div>`;
}
html += '</div>';
results.innerHTML = html;
log(`✅ Dark scan complete: ${d.osint?.dns?.length || 0} DNS records`, 'ok');
}
}
async function darkScrape() {
const url = document.getElementById('darkTarget').value.trim();
if (!url) return;
const fullUrl = url.startsWith('http') ? url : 'https://' + url;
log(`🕷️ Deep Scrape: ${fullUrl}`, 'info');
const data = await api('dark_scrape', { url: fullUrl });
const results = document.getElementById('darkResults');
if (data?.scrape) {
const s = data.scrape;
let html = `
<div class="dark-item" style="color:var(--primary)">📄 ${s.title || 'No title'} (${(s.size/1024).toFixed(1)}KB)</div>
<div class="dark-item">🔧 Tech: ${s.technologies?.join(', ') || 'None detected'}</div>
<div class="dark-item">📧 Emails: ${s.emails?.length || 0} found</div>
<div class="dark-item">📞 Phones: ${s.phones?.length || 0} found</div>
<div class="dark-item">🔗 Links: ${s.links?.length || 0} extracted</div>
`;
if (s.emails?.length) html += `<div class="dark-item" style="color:var(--warning)">${s.emails.join(', ')}</div>`;
results.innerHTML = html;
log(`✅ Scrape: ${s.technologies?.length} techs, ${s.emails?.length} emails, ${s.links?.length} links`, 'ok');
}
}
// ═══ COLLECTIVE UNCONSCIOUS ═══
async function queryCollective() {
const query = document.getElementById('collectiveQuery').value.trim();
if (!query) return;
log(`🧬 Collective query: "${query}"`, 'info');
const data = await api('collective', { q: query });
const results = document.getElementById('collectiveResults');
if (data?.knowledge) {
const k = data.knowledge;
let html = `<div style="padding:8px;background:var(--surface2);border-radius:8px;margin-bottom:8px">
<div style="font-size:11px;color:var(--secondary);font-family:var(--mono)">Confidence: ${k.fusion?.confidence || 0}% | KB: ${k.fusion?.total_kb || 0} | HAMID: ${k.fusion?.total_hamid || 0} | Winners: ${k.fusion?.total_winners || 0}</div>
</div>`;
if (k.kb?.length) {
k.kb.forEach(item => {
html += `<div style="padding:8px;border-bottom:1px solid var(--border);font-size:11px">
<div style="color:var(--primary);font-weight:600">${item.title || 'KB Entry'}</div>
<div style="color:var(--text2);margin-top:4px">${(item.content || '').substring(0, 200)}...</div>
</div>`;
});
}
if (k.fusion?.synthesis) {
html += `<div style="padding:8px;margin-top:8px;background:var(--secondary-glow);border-radius:8px;font-size:11px;border:1px solid rgba(123,104,238,0.3)">
<div style="color:var(--secondary);font-weight:600">🧬 Synthèse Collective</div>
<div style="margin-top:4px;color:var(--text2)">${k.fusion.synthesis.substring(0, 500)}</div>
</div>`;
}
results.innerHTML = html;
log(`✅ Collective: ${k.fusion?.confidence}% confidence, ${k.fusion?.total_kb} KB matches`, 'ok');
}
}
// ═══ SOCIAL ═══
function selectPlatform(btn, platform) {
document.querySelectorAll('.social-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentPlatform = platform;
}
async function generateSocial() {
const topic = document.getElementById('socialTopic').value.trim();
if (!topic) return;
log(`📡 Social generate: ${currentPlatform} — "${topic}"`, 'info');
const data = await api('social_generate', { _body: { action: 'social_generate', topic, platform: currentPlatform } });
if (data?.social?.content) {
document.getElementById('socialOutput').textContent = data.social.content;
log(`✅ Content generated for ${currentPlatform}`, 'ok');
}
}
// ═══ GO LIVE ═══
async function goLive() {
const hero = document.getElementById('hero');
hero.style.background = 'linear-gradient(135deg, #001a0d 0%, #0a1a2e 50%, #1a0a1a 100%)';
hero.style.borderColor = 'var(--primary)';
log('', '');
log('╔══════════════════════════════════════════════════════╗', 'ok');
log('║ WEVIA NEXUS ULTIMATE 2026 — GO LIVE ║', 'ok');
log('║ CODENAME: SINGULARITY ║', 'ok');
log('║ WEVAL Consulting · Casablanca · Maroc ║', 'ok');
log('╚══════════════════════════════════════════════════════╝', 'ok');
log('', '');
const checks = [
['Brain Engine (11 Providers)', true],
['GPU Server RTX 4000 Ada (7 Models)', true],
['Dark Intelligence Module', true],
['Inconscient Collectif (RAG + KB 1710)', true],
['Réseaux Sociaux Engine', true],
['Arsenal Ops (1415 Modules)', true],
['Email Pipeline (PMTA + O365)', true],
['Sentinel Exec & Monitor', true],
['Vision & OCR', true],
['Voice & TTS', true],
['Code Sandbox', true],
['Doc Generator (PDF/Word/Excel/PPT)', true],
['Knowledge Base (1710 entries)', true],
['7 Serveurs Interconnectés', true],
['39 Crons Automatiques', true],
];
for (const [name, ok] of checks) {
await new Promise(r => setTimeout(r, 200));
log(` ${ok ? '✅' : '❌'} ${name}`, ok ? 'ok' : 'err');
}
log('', '');
log('🚀 ════════════════════════════════════════════════', 'ok');
log('🚀 SINGULARITY IS LIVE — L\'IA DE 2026 EST ACTIVE', 'ok');
log('🚀 ════════════════════════════════════════════════', 'ok');
document.getElementById('statusText').textContent = '⚡ SINGULARITY LIVE';
}
// ═══ ARCHITECTURE ═══
function showArchitecture() {
const msgs = document.getElementById('chatMessages');
msgs.innerHTML += `<div class="msg-ai" style="font-family:var(--mono);font-size:11px;white-space:pre;line-height:1.4">
<span style="color:var(--primary)">╔══════════════════════════════════════════════════════════╗
║ WEVIA NEXUS ULTIMATE 2026 — ARCHITECTURE ║
╠══════════════════════════════════════════════════════════╣
║ ║
║ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ║
║ │ DARK │ │COLLEC│ │SOCIAL│ │BRAIN │ │ARSEN.│ ║
║ │INTEL │ │UNCON.│ │NETWK │ │ENGINE│ │ OPS │ ║
║ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ ║
║ └──────┬──┴────┬────┴────┬────┴────┬────┘ ║
║ │ MASTER ORCHESTRATOR API │ ║
║ └────────────┬───────────────┘ ║
║ ┌──────────┬───────┤────────┬──────────┐ ║
║ ▼ ▼ ▼ ▼ ▼ ║
║ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ║
║ │89.167│ │46.62 │ │88.198│ │HW1/2 │ │ OVH │ ║
║ │WEVADS│ │WEVIA │ │ GPU │ │HUAWEI│ │TRACK │ ║
║ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ ║
║ ║
║ PROVIDERS: Cerebras│Groq│SambaNova│Ollama│DeepSeek ║
║ Gemini│Mistral│Cohere│Hyperbolic ║
╚══════════════════════════════════════════════════════════╝</span>
</div>`;
msgs.scrollTop = msgs.scrollHeight;
}
// ═══ MODULE ACTIVATION ═══
function activateModule(id) {
log(`🔌 Module activated: ${id}`, 'info');
// Scroll to relevant section based on module
const scrollTargets = {
dark: 'darkTarget', collective: 'collectiveQuery', social: 'socialTopic',
brain: 'chatInput', email: 'terminal', sentinel: 'terminal'
};
const target = scrollTargets[id];
if (target) document.getElementById(target)?.focus();
}
// ═══ LAUNCH ═══
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>