Files
html/intelligence-growth.html.gold-19avr-opus
opus ab71e7fdc4
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
AUTO-BACKUP 20260419-2000
2026-04-19 20:00:04 +02:00

404 lines
23 KiB
Plaintext

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVAL · Intelligence &amp; Growth Insights — DarkScout + WEVL Predict</title>
<style>
:root{--bg-0:#05060a;--bg-1:#0b0d15;--bg-2:#11141f;--bg-3:#171b2a;--border:rgba(99,102,241,0.15);--border-h:rgba(99,102,241,0.35);--text:#e2e8f0;--dim:#94a3b8;--mute:#64748b;--accent:#14b8a6;--accent2:#6366f1;--purple:#a855f7;--cyan:#06b6d4;--ok:#22c55e;--warn:#f59e0b;--err:#ef4444;--rose:#f43f5e;--gold:#eab308}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',system-ui,sans-serif;background:radial-gradient(ellipse at top,#0f1420,#05060a 60%);color:var(--text);min-height:100vh;font-size:13px;line-height:1.5}
.container{max-width:1760px;margin:0 auto;padding:24px 28px 80px}
header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:18px;padding-bottom:14px;border-bottom:1px solid var(--border)}
h1{font-size:25px;font-weight:800;background:linear-gradient(90deg,#22d3ee,#a855f7,#eab308);-webkit-background-clip:text;background-clip:text;color:transparent;letter-spacing:-0.4px}
.sub{color:var(--dim);font-size:12.5px;margin-top:4px}
.actions{display:flex;gap:8px}
.btn{padding:7px 13px;background:var(--bg-2);border:1px solid var(--border);color:var(--text);border-radius:8px;font-size:11.5px;cursor:pointer;text-decoration:none;transition:all .2s}
.btn:hover{border-color:var(--accent);color:var(--accent)}
.pulse{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 0 rgba(34,197,94,.7);animation:pulse 2s infinite;margin-right:6px}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,.7)}70%{box-shadow:0 0 0 8px rgba(34,197,94,0)}}
/* KPI strip */
.kpi-strip{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin-bottom:16px}
@media(max-width:1100px){.kpi-strip{grid-template-columns:repeat(3,1fr)}}
.kpi-big{background:var(--bg-1);border:1px solid var(--border);border-radius:10px;padding:12px 14px;position:relative;border-left:3px solid var(--accent2)}
.kpi-big.gold{border-left-color:var(--gold)}
.kpi-big.danger{border-left-color:var(--err)}
.kpi-big.cyan{border-left-color:var(--cyan)}
.kpi-big.rose{border-left-color:var(--rose)}
.kpi-big.green{border-left-color:var(--ok)}
.kpi-lbl{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;font-weight:600}
.kpi-val{font-size:22px;font-weight:800;margin-top:4px;line-height:1;font-family:'JetBrains Mono',monospace}
.kpi-val.gold{background:linear-gradient(135deg,var(--gold),var(--warn));-webkit-background-clip:text;background-clip:text;color:transparent}
.kpi-sub{font-size:10px;color:var(--mute);margin-top:2px}
/* Section */
.section{background:var(--bg-1);border:1px solid var(--border);border-radius:12px;padding:18px;margin-bottom:16px}
.sec-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--bg-3)}
.sec-title{font-size:14px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px}
.sec-badge{font-size:10px;padding:3px 8px;border-radius:8px;font-weight:700}
.sec-badge.danger{background:rgba(239,68,68,0.2);color:#fca5a5}
.sec-badge.ok{background:rgba(34,197,94,0.18);color:#86efac}
.sec-badge.info{background:rgba(99,102,241,0.18);color:#a5b4fc}
.sec-badge.gold{background:rgba(234,179,8,0.18);color:#fde047}
/* Competitors grid */
.comp-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px}
.comp-card{background:var(--bg-2);border-radius:10px;padding:13px;border-left:3px solid var(--dim);transition:all .2s}
.comp-card:hover{border-color:var(--border-h);background:var(--bg-3)}
.comp-card.high{border-left-color:var(--err)}
.comp-card.medium{border-left-color:var(--warn)}
.comp-card.low{border-left-color:var(--ok)}
.comp-name{font-size:13px;font-weight:700;color:var(--text);display:flex;justify-content:space-between;align-items:flex-start}
.comp-threat{font-size:9px;padding:2px 6px;border-radius:5px;font-weight:700;text-transform:uppercase}
.comp-threat.high{background:rgba(239,68,68,0.2);color:#fca5a5}
.comp-threat.medium{background:rgba(245,158,11,0.2);color:#fbbf24}
.comp-threat.low{background:rgba(34,197,94,0.18);color:#86efac}
.comp-meta{font-size:10.5px;color:var(--dim);margin-top:3px}
.comp-section{font-size:10.5px;margin-top:7px;line-height:1.4}
.comp-section strong{color:var(--accent);font-weight:600}
.comp-diff{margin-top:7px;padding:6px 8px;background:rgba(20,184,166,0.08);border-left:2px solid var(--accent);border-radius:4px;font-size:10.5px;color:#5eead4}
.comp-score{display:inline-block;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--mute)}
/* Market signals */
.market-signals{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px;margin-top:12px;padding-top:12px;border-top:1px dashed var(--border)}
.market-sig{background:var(--bg-2);padding:8px 10px;border-radius:6px;font-size:10.5px}
.market-sig .s{color:var(--dim)}
.market-sig .v{font-weight:700;margin-top:2px}
.market-sig .v.up{color:var(--ok)}
.market-sig .v.down{color:var(--err)}
/* Innovations timeline */
.innov-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px}
.innov-card{background:var(--bg-2);border-radius:8px;padding:12px;border-left:3px solid var(--cyan);transition:all .2s}
.innov-card:hover{background:var(--bg-3);transform:translateX(2px)}
.innov-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}
.innov-name{font-size:12.5px;font-weight:700;color:var(--text)}
.innov-date{font-size:9.5px;color:var(--mute);font-family:'JetBrains Mono',monospace}
.innov-type{font-size:9.5px;padding:1px 6px;border-radius:4px;background:rgba(168,85,247,0.15);color:#d4a7fa;display:inline-block;margin-top:3px;text-transform:uppercase;letter-spacing:0.3px;font-weight:600}
.innov-desc{font-size:11px;color:var(--dim);margin-top:6px;line-height:1.4}
.innov-value{font-size:10.5px;color:#5eead4;margin-top:5px;font-weight:500}
.innov-stars{color:var(--gold);font-size:11px;margin-top:3px;letter-spacing:1px}
/* Agility agents */
.agil-wrap{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:8px}
.agil-card{background:var(--bg-2);border-radius:8px;padding:10px 12px;border-left:3px solid var(--purple);display:flex;flex-direction:column;gap:4px}
.agil-card.high{border-left-color:var(--warn)}
.agil-card.medium{border-left-color:var(--cyan)}
.agil-card.low{border-left-color:var(--mute)}
.agil-head{display:flex;justify-content:space-between;align-items:flex-start;font-size:12px}
.agil-name{font-weight:700;color:var(--text)}
.agil-framework{font-size:10px;padding:1px 6px;border-radius:4px;background:rgba(99,102,241,0.15);color:#a5b4fc}
.agil-role{font-size:10.5px;color:var(--dim);line-height:1.4;margin-top:2px}
.agil-save{font-size:10.5px;color:var(--gold);font-family:'JetBrains Mono',monospace;margin-top:3px;font-weight:600}
.agil-status{font-size:9.5px;color:var(--warn);text-transform:uppercase;letter-spacing:0.4px;font-weight:700;margin-top:2px}
.agil-status.partial{color:var(--cyan)}
/* Chatbots / Leads */
.chat-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:12px}
.chat-stat{text-align:center;padding:10px;background:var(--bg-2);border-radius:8px;border-top:2px solid var(--accent)}
.chat-stat.warn{border-top-color:var(--warn)}
.chat-stat.ok{border-top-color:var(--ok)}
.chat-stat-v{font-size:22px;font-weight:800;font-family:'JetBrains Mono',monospace;color:var(--text)}
.chat-stat-l{font-size:10px;color:var(--dim);margin-top:3px;text-transform:uppercase;letter-spacing:0.3px}
.chat-funnel{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-top:10px}
.chat-funnel-step{background:var(--bg-2);padding:10px;border-radius:6px;text-align:center;border-bottom:2px solid var(--cyan)}
.chat-funnel-pct{font-size:18px;font-weight:800;color:var(--cyan);font-family:'JetBrains Mono',monospace}
.chat-funnel-lbl{font-size:10px;color:var(--dim);margin-top:3px}
.chat-alerts{margin-top:12px;padding:10px;background:rgba(245,158,11,0.08);border-left:2px solid var(--warn);border-radius:4px;font-size:11px;color:#fbbf24;line-height:1.5}
.chat-alerts strong{display:block;margin-bottom:3px;color:#fde047}
/* Opportunities */
.oppo-wrap{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:8px}
.oppo-card{background:var(--bg-2);border-radius:8px;padding:10px 12px;border-left:3px solid var(--accent2)}
.oppo-card.critical{border-left-color:var(--err)}
.oppo-card.high{border-left-color:var(--warn)}
.oppo-card.medium{border-left-color:var(--cyan)}
.oppo-head{display:flex;justify-content:space-between;align-items:flex-start;font-size:12px;margin-bottom:4px}
.oppo-src{font-size:9.5px;padding:1px 6px;border-radius:4px;background:rgba(168,85,247,0.15);color:#d4a7fa}
.oppo-signal{font-weight:600;color:var(--text);font-size:12px;margin-top:4px}
.oppo-action{font-size:10.5px;color:var(--dim);margin-top:3px;line-height:1.4}
.oppo-foot{display:flex;justify-content:space-between;align-items:center;margin-top:6px;font-size:10px}
.oppo-val{font-family:'JetBrains Mono',monospace;font-weight:700;color:var(--gold)}
.oppo-urg{text-transform:uppercase;font-weight:700;letter-spacing:0.4px}
.oppo-urg.critical{color:#fca5a5}
.oppo-urg.high{color:#fbbf24}
.oppo-urg.medium{color:#7dd3fc}
/* Value KPIs */
.vkpi-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px}
.vkpi-cell{background:var(--bg-2);padding:10px 12px;border-radius:8px;border-left:2px solid var(--dim)}
.vkpi-cell.ok{border-left-color:var(--ok)}
.vkpi-cell.warn{border-left-color:var(--warn)}
.vkpi-cell.critical{border-left-color:var(--err)}
.vkpi-lbl{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:0.3px;font-weight:600}
.vkpi-v{font-size:17px;font-weight:800;font-family:'JetBrains Mono',monospace;color:var(--text);margin-top:3px}
.vkpi-target{font-size:9.5px;color:var(--mute);margin-top:2px}
.loading{text-align:center;padding:40px;color:var(--dim)}
.spinner{width:34px;height:34px;border:3px solid var(--bg-3);border-top-color:var(--accent);border-radius:50%;margin:0 auto 12px;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.clock{font-family:'JetBrains Mono',monospace;color:var(--accent);font-size:11px;margin-top:3px}
</style>
</head>
<body>
<div class="container">
<header>
<div>
<h1><span class="pulse"></span>🌐 Intelligence &amp; Growth Insights</h1>
<div class="sub">DarkScout compétiteurs · WEVL Predict innovations · Agility agents gap · Lead Capture Hub · Opportunities Watch</div>
<div class="clock" id="clock">—</div>
</div>
<div class="actions">
<a href="/weval-technology-platform.html" class="btn">🏠 WTP</a>
<a href="/dg-command-center.html" class="btn">🎖️ DG Center</a>
<a href="/agent-roi-simulator.html" class="btn">🧮 ROI Sim</a>
<button class="btn" onclick="load()">↻ Refresh</button>
</div>
</header>
<!-- KPI STRIP -->
<div class="kpi-strip">
<div class="kpi-big danger"><div class="kpi-lbl">🎯 Compétiteurs trackés</div><div class="kpi-val" id="kpi-comp">—</div><div class="kpi-sub"><span id="kpi-comp-high">—</span> high threat</div></div>
<div class="kpi-big green"><div class="kpi-lbl">⚡ Innovations 24h</div><div class="kpi-val gold" id="kpi-innov24">—</div><div class="kpi-sub">total <span id="kpi-innov">—</span></div></div>
<div class="kpi-big cyan"><div class="kpi-lbl">🎭 Agility Agents GAP</div><div class="kpi-val" id="kpi-agil">—</div><div class="kpi-sub"><span id="kpi-agil-fte">—</span> FTE savings/an</div></div>
<div class="kpi-big rose"><div class="kpi-lbl">🤖 Chatbots deployed</div><div class="kpi-val" id="kpi-chat">—</div><div class="kpi-sub">sur <span id="kpi-chat-tot">—</span> planifiés</div></div>
<div class="kpi-big gold"><div class="kpi-lbl">💰 Opportunities pipeline</div><div class="kpi-val gold" id="kpi-oppo">—</div><div class="kpi-sub"><span id="kpi-oppo-urg">—</span> urgent</div></div>
<div class="kpi-big"><div class="kpi-lbl">📐 Value KPIs</div><div class="kpi-val" id="kpi-vkpis">—</div><div class="kpi-sub">cross-cutting</div></div>
</div>
<!-- DARKSCOUT -->
<div class="section">
<div class="sec-head">
<div class="sec-title">🎯 DarkScout — Veille concurrentielle temps-réel</div>
<div class="sec-badge danger" id="ds-count">—</div>
</div>
<div class="comp-grid" id="comp-grid"><div class="loading"><div class="spinner"></div></div></div>
<div class="market-signals" id="market-signals"></div>
</div>
<!-- INNOVATIONS -->
<div class="section">
<div class="sec-head">
<div class="sec-title">⚡ WEVL Predict — Innovations live</div>
<div class="sec-badge gold" id="innov-velocity">—</div>
</div>
<div class="innov-grid" id="innov-grid"><div class="loading"><div class="spinner"></div></div></div>
</div>
<!-- AGILITY AGENTS GAP -->
<div class="section">
<div class="sec-head">
<div class="sec-title">🎭 Agility Agents Gap — Scrum/SAFe/Agile/DevOps</div>
<div class="sec-badge info" id="agil-total-sav">—</div>
</div>
<div class="agil-wrap" id="agil-wrap"><div class="loading"><div class="spinner"></div></div></div>
</div>
<!-- LEAD CAPTURE HUB -->
<div class="section">
<div class="sec-head">
<div class="sec-title">🤖 Lead Capture Hub — 40 chatbots publics</div>
<div class="sec-badge info" id="lch-status">—</div>
</div>
<div class="chat-stats" id="chat-stats"></div>
<div style="font-size:11px;color:var(--dim);margin-top:10px;margin-bottom:6px;font-weight:600">Conversion funnel lead :</div>
<div class="chat-funnel" id="chat-funnel"></div>
<div class="chat-alerts" id="chat-alerts"></div>
</div>
<!-- OPPORTUNITIES -->
<div class="section">
<div class="sec-head">
<div class="sec-title">💼 Opportunities Watch — LinkedIn + Web + Referrals</div>
<div class="sec-badge gold" id="oppo-val">—</div>
</div>
<div class="oppo-wrap" id="oppo-wrap"><div class="loading"><div class="spinner"></div></div></div>
</div>
<!-- VALUE KPIS -->
<div class="section">
<div class="sec-head">
<div class="sec-title">📐 Value KPIs cross-cutting (exploitation force archi)</div>
<div class="sec-badge info">12 KPIs</div>
</div>
<div class="vkpi-grid" id="vkpi-grid"><div class="loading"><div class="spinner"></div></div></div>
</div>
</div>
<script>
const API = '/api/wevia-v71-intelligence-growth.php';
let DATA = null;
function clockTick(){const d=new Date();document.getElementById('clock').textContent=d.toLocaleDateString('fr-FR')+' · '+d.toLocaleTimeString('fr-FR')+' · auto-refresh 30s';}
setInterval(clockTick,1000); clockTick();
function fmt(n){if(!n&&n!==0)return'—';if(Math.abs(n)>=1000000)return(n/1000000).toFixed(2)+'M';if(Math.abs(n)>=1000)return(n/1000).toFixed(1)+'k';return Math.round(n);}
async function load(){
try {
const r = await fetch(API+'?t='+Date.now());
DATA = await r.json();
render();
} catch(e) { console.error(e); }
}
function render(){
if(!DATA) return;
const s = DATA.summary;
// KPI strip
document.getElementById('kpi-comp').textContent = s.competitors_tracked;
document.getElementById('kpi-comp-high').textContent = s.competitors_high_threat;
document.getElementById('kpi-innov24').textContent = s.innovations_last_24h;
document.getElementById('kpi-innov').textContent = s.innovations_total;
document.getElementById('kpi-agil').textContent = s.agility_agents_gap;
document.getElementById('kpi-agil-fte').textContent = s.agility_fte_savings_year;
document.getElementById('kpi-chat').textContent = s.chatbots_deployed;
document.getElementById('kpi-chat-tot').textContent = s.chatbots_total;
document.getElementById('kpi-oppo').textContent = fmt(s.opportunities_total_value_keur*1000) + '€';
document.getElementById('kpi-oppo-urg').textContent = s.opportunities_high_urgency;
document.getElementById('kpi-vkpis').textContent = s.value_kpis_count;
// DarkScout
const ds = DATA.darkscout_competitors;
document.getElementById('ds-count').textContent = ds.tracked_count + ' compétiteurs · MAJ ' + new Date(ds.updated_at).toLocaleTimeString('fr-FR');
document.getElementById('comp-grid').innerHTML = ds.competitors.map(c => `
<div class="comp-card ${c.threat_level}">
<div class="comp-name">${c.name}<span class="comp-threat ${c.threat_level}">${c.threat_level}</span></div>
<div class="comp-meta">${c.category} · ${c.market}</div>
<div class="comp-section"><strong>💰 Prix :</strong> ${c.pricing_estimate}</div>
<div class="comp-section"><strong>🎯 Positioning :</strong> ${c.positioning}</div>
<div class="comp-section"><strong>📰 Moves récents :</strong> ${c.recent_moves.slice(0,2).join(' · ')}</div>
<div class="comp-diff">🛡 <strong>WEVAL differentiator :</strong> ${c.weval_differentiator}</div>
<div class="comp-score">risk_score ${c.risk_score}/25</div>
</div>
`).join('');
// Market signals
const ms = ds.market_signals;
document.getElementById('market-signals').innerHTML = Object.entries(ms).map(([k,v]) => {
const dir = v.startsWith('UP') ? 'up' : v.startsWith('DOWN') ? 'down' : '';
return `<div class="market-sig"><div class="s">${k.replace(/_/g,' ')}</div><div class="v ${dir}">${dir==='up'?'↑':dir==='down'?'↓':''} ${v}</div></div>`;
}).join('');
// Innovations
const innov = DATA.wevl_predict_innovations;
document.getElementById('innov-velocity').textContent = innov.innovation_velocity;
document.getElementById('innov-grid').innerHTML = innov.innovations.map(i => `
<div class="innov-card">
<div class="innov-head">
<div class="innov-name">${i.name}</div>
<div class="innov-date">${i.date}</div>
</div>
<span class="innov-type">${i.type}</span>
<div class="innov-desc">${i.description}</div>
<div class="innov-value">💎 ${i.value_prop}</div>
<div class="innov-stars">${'★'.repeat(i.novelty)}${'☆'.repeat(5-i.novelty)}</div>
</div>
`).join('');
// Agility agents
const ag = DATA.agility_agents_gap;
document.getElementById('agil-total-sav').textContent = ag.total_fte_saved_year + ' FTE / ' + fmt(ag.total_savings_year_eur) + '€ savings potentiels';
document.getElementById('agil-wrap').innerHTML = ag.agents.map(a => `
<div class="agil-card ${a.priority}">
<div class="agil-head"><div class="agil-name">${a.name}</div><div class="agil-framework">${a.framework}</div></div>
<div class="agil-role">${a.role}</div>
<div class="agil-save">💰 ${a.savings_fte_year} FTE/an (~${Math.round(a.savings_fte_year*90)}k€)</div>
<div class="agil-status ${a.status}">${a.status.replace('_',' ')}</div>
</div>
`).join('');
// Lead Capture Hub
const lch = DATA.lead_capture_hub;
document.getElementById('lch-status').textContent = lch.chatbots_deployed + '/' + lch.chatbots_total + ' déployés';
document.getElementById('chat-stats').innerHTML = `
<div class="chat-stat ok"><div class="chat-stat-v">${lch.chatbots_deployed}</div><div class="chat-stat-l">Deployed</div></div>
<div class="chat-stat warn"><div class="chat-stat-v">${lch.chatbots_planned}</div><div class="chat-stat-l">À wirer</div></div>
<div class="chat-stat"><div class="chat-stat-v">${lch.leads_captured_7d}</div><div class="chat-stat-l">Leads 7j</div></div>
<div class="chat-stat"><div class="chat-stat-v">${lch.leads_captured_month}</div><div class="chat-stat-l">Leads mois</div></div>
`;
document.getElementById('chat-funnel').innerHTML = lch.conversion_signals.map(cs => `
<div class="chat-funnel-step">
<div class="chat-funnel-pct">${cs.rate_pct}%</div>
<div class="chat-funnel-lbl">${cs.signal}</div>
</div>
`).join('');
document.getElementById('chat-alerts').innerHTML = '<strong>⚠️ Alertes :</strong>' + lch.alerts.map(a => '• ' + a).join('<br>');
// Opportunities
const ow = DATA.opportunities_watch;
document.getElementById('oppo-val').textContent = fmt(DATA.summary.opportunities_total_value_keur*1000) + '€ pipeline';
document.getElementById('oppo-wrap').innerHTML = ow.opportunities.map(o => `
<div class="oppo-card ${o.urgency}">
<div class="oppo-head"><span class="oppo-src">${o.source}</span></div>
<div class="oppo-signal">${o.signal}</div>
<div class="oppo-action">→ ${o.action}</div>
<div class="oppo-foot">
<span class="oppo-val">${o.estimated_value_keur?fmt(o.estimated_value_keur*1000)+'€':'—'}</span>
<span class="oppo-urg ${o.urgency}">${o.urgency}</span>
</div>
</div>
`).join('');
// Value KPIs
document.getElementById('vkpi-grid').innerHTML = DATA.value_kpis.map(k => `
<div class="vkpi-cell ${k.s}">
<div class="vkpi-lbl">${k.k}</div>
<div class="vkpi-v">${k.v}${k.u}</div>
<div class="vkpi-target">target ${k.t}${k.u}</div>
</div>
`).join('');
}
load();
setInterval(load, 30000);
</script>
<script>
/* V75 AVATAR UNIFIER — Meeting-rooms emoji style (Opus 19avr) */
(function() {
if (window.__WEVAL_AVATAR_V75) return;
window.__WEVAL_AVATAR_V75 = true;
const REG_URL = '/api/agent-avatars-v75.json';
const SVG_EP = '/api/agent-avatar-svg.php';
function emojiSVGUrl(name, emoji) {
return SVG_EP + '?n=' + encodeURIComponent(name) + '&e=' + encodeURIComponent(emoji);
}
fetch(REG_URL + '?t=' + Date.now()).then(r => r.json()).then(REG => {
function getAvatarUrl(name) {
const rec = REG[name];
if (!rec) return null;
if (typeof rec === 'object' && rec.svg) return rec.svg;
if (typeof rec === 'object' && rec.emoji) return emojiSVGUrl(name, rec.emoji);
return typeof rec === 'string' ? rec : null;
}
function findCI(key) {
const lower = key.toLowerCase();
for (const k of Object.keys(REG)) if (k.toLowerCase() === lower) return k;
return null;
}
function apply() {
document.querySelectorAll('img').forEach(img => {
const key = img.alt || img.dataset.agent || img.dataset.name || img.title || '';
if (!key) return;
let url = getAvatarUrl(key);
if (!url) { const alt = findCI(key); if (alt) url = getAvatarUrl(alt); }
if (url && img.src !== url && !img.src.endsWith(url)) {
img.src = url;
img.setAttribute('data-weval-v75', '1');
}
});
document.querySelectorAll('[data-agent]:not([data-weval-v75-applied])').forEach(el => {
const name = el.dataset.agent;
const url = getAvatarUrl(name);
if (!url) return;
const img = document.createElement('img');
img.src = url; img.alt = name; img.title = name;
img.className = 'v75-avatar';
img.style.cssText = 'width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle;background:transparent';
el.setAttribute('data-weval-v75-applied', '1');
el.prepend(img);
});
}
apply();
setTimeout(apply, 400); setTimeout(apply, 1200); setTimeout(apply, 3000);
const mo = new MutationObserver(() => apply());
mo.observe(document.body, {childList: true, subtree: true});
setTimeout(() => mo.disconnect(), 20000);
console.log('[V75 AvatarUnifier] applied from', Object.keys(REG).length, 'agents');
}).catch(e => console.warn('[V75] fetch failed', e));
})();
</script>
</body>
</html>