// ===================================================== // SentinelAI - Knowledge Graph Reputationnel Engine v3.0 // Dimensions: Trust · Authority · Sentiment · Influence · E-E-A-T // ===================================================== const state = { startTime: Date.now(), domain: '', entities: [], keywords: [], competitors: [], dimensions: ['trust', 'authority', 'sentiment', 'influence', 'eeat'], kgNodes: [], kgLinks: [], scores: { trust: 0, authority: 0, sentiment: 0, influence: 0, eeat: 0, global: 0 }, scoreBreakdown: {}, logs: [], packets: [], canvasAnimId: null, hoveredNode: null, analyzing: false, analysisData: null, }; const ENTITY_TYPES = [ { type: 'brand', label: 'Marque', icon: 'building-2', color: 'violet' }, { type: 'person', label: 'Personne', icon: 'user', color: 'cyan' }, { type: 'organization', label: 'Organisation', icon: 'briefcase', color: 'purple' }, { type: 'concept', label: 'Concept', icon: 'lightbulb', color: 'amber' }, { type: 'product', label: 'Produit', icon: 'package', color: 'green' }, { type: 'location', label: 'Localisation', icon: 'map-pin', color: 'blue' }, { type: 'competitor', label: 'Concurrent', icon: 'swords', color: 'red' }, { type: 'influencer', label: 'Influenceur', icon: 'star', color: 'fuchsia' }, ]; const RELATION_TYPES = [ { type: 'owns', label: 'possède', color: '#8b5cf6' }, { type: 'works-for', label: 'travaille pour', color: '#06b6d4' }, { type: 'competes-with', label: 'concurrence', color: '#ef4444' }, { type: 'collaborates', label: 'collabore', color: '#22c55e' }, { type: 'mentions', label: 'mentionne', color: '#f59e0b' }, { type: 'endorses', label: 'recommande', color: '#a855f7' }, { type: 'related-to', label: 'lié à', color: '#64748b' }, { type: 'influences', label: 'influence', color: '#ec4899' }, ]; const CognitiveAI = { neural: { layers: [5, 10, 10, 8, 3], activations: [], weights: [], biases: [], pulses: [] }, active: false, automation: false, iterations: 0, decisions: [], patterns: [], predictions: [], autoActions: 0, confidence: 0, learningRate: 0.01, automationInterval: null, neuralAnimId: null, geneticPopulation: [], geneticGeneration: 0, bestFitness: 0, initNeural() { this.neural.weights = []; this.neural.biases = []; for (let i = 0; i < this.neural.layers.length - 1; i++) { const lw = [], lb = []; for (let j = 0; j < this.neural.layers[i + 1]; j++) { const nw = []; for (let k = 0; k < this.neural.layers[i]; k++) nw.push(Math.random() * 2 - 1); lw.push(nw); lb.push(Math.random() * 0.5 - 0.25); } this.neural.weights.push(lw); this.neural.biases.push(lb); } this.neural.activations = this.neural.layers.map(n => new Array(n).fill(0)); this.neural.pulses = []; }, sigmoid(x) { return 1 / (1 + Math.exp(-x)); }, forwardPass(inputs) { this.neural.activations[0] = inputs.slice(); for (let i = 0; i < this.neural.weights.length; i++) { const next = []; for (let j = 0; j < this.neural.weights[i].length; j++) { let sum = this.neural.biases[i][j]; for (let k = 0; k < this.neural.weights[i][j].length; k++) sum += this.neural.activations[i][k] * this.neural.weights[i][j][k]; next.push(this.sigmoid(sum)); } this.neural.activations[i + 1] = next; for (let j = 0; j < next.length; j++) { if (Math.random() < 0.3) this.neural.pulses.push({ layer: i, fromNeuron: Math.floor(Math.random() * this.neural.layers[i]), toNeuron: j, progress: 0, speed: 0.03 + Math.random() * 0.02, intensity: next[j] }); } } return this.neural.activations[this.neural.activations.length - 1]; }, recognizePatterns() { const patterns = []; const isolated = state.kgNodes.filter(n => n.isolated); if (isolated.length > 0) patterns.push({ type: 'Isolated Entities', description: `${isolated.length} entité(s) isolée(s) sans relations`, confidence: 0.85 + Math.random() * 0.1, severity: 'critical' }); const negativeEntities = state.kgNodes.filter(n => n.sentiment < 30); if (negativeEntities.length > 0) patterns.push({ type: 'Negative Sentiment Cluster', description: `${negativeEntities.length} entité(s) avec sentiment négatif`, confidence: 0.80, severity: 'high' }); if (state.scores.trust < 50) patterns.push({ type: 'Trust Deficit', description: `Score TRUST ${state.scores.trust}/100 — confiance insuffisante`, confidence: 0.82, severity: 'high' }); if (state.scores.authority < 50) patterns.push({ type: 'Authority Gap', description: `Score AUTHORITY ${state.scores.authority}/100 — autorité faible`, confidence: 0.80, severity: 'high' }); if (state.scores.eeat < 50) patterns.push({ type: 'E-E-A-T Weakness', description: `Score E-E-A-T ${state.scores.eeat}/100 — expertise non démontrée`, confidence: 0.78, severity: 'high' }); if (state.scores.influence < 40) patterns.push({ type: 'Low Influence', description: `Score INFLUENCE ${state.scores.influence}/100 — portée limitée`, confidence: 0.75, severity: 'medium' }); const competitors = state.kgNodes.filter(n => n.type === 'competitor'); if (competitors.length > state.kgNodes.filter(n => n.type === 'brand').length) patterns.push({ type: 'Competitor Dominance', description: `${competitors.length} concurrents vs peu d'entités principales`, confidence: 0.72, severity: 'medium' }); const density = state.kgLinks.length / Math.max(state.kgNodes.length, 1); if (density < 1.5) patterns.push({ type: 'Low Graph Density', description: `Densité ${density.toFixed(2)} — objectif > 2.0`, confidence: 0.80, severity: 'medium' }); this.patterns = patterns; return patterns; }, predictWeaknesses() { const predictions = []; if (state.scores.global < 250) predictions.push({ prediction: `Score global ${state.scores.global}/500 — réputation en danger`, probability: 0.85, timeframe: '30 jours', action: 'Activer auto-remédiation cognitive' }); if (state.scores.trust < 40) predictions.push({ prediction: 'TRUST critique — perte de confiance clients probable', probability: 0.80, timeframe: '14 jours', action: 'Renforcer signaux de confiance (avis, certifications)' }); if (state.scores.sentiment < 40) predictions.push({ prediction: 'SENTIMENT négatif — bad reputation risk', probability: 0.82, timeframe: 'En cours', action: 'Campagne de gestion de réputation en ligne' }); if (state.scores.eeat < 40) predictions.push({ prediction: 'E-E-A-T faible — non éligible pour AI Overviews', probability: 0.78, timeframe: '60 jours', action: 'Publier contenu expert + credentials' }); if (state.kgNodes.filter(n => n.isolated).length > 3) predictions.push({ prediction: `${state.kgNodes.filter(n => n.isolated).length} entités isolées — graphe fragmenté`, probability: 0.75, timeframe: 'Immédiat', action: 'Créer relations entre entités isolées' }); if (state.scores.influence < 30) predictions.push({ prediction: 'INFLUENCE très faible — invisibilité dans le graphe', probability: 0.70, timeframe: '90 jours', action: 'Stratégie netlinking + relations publiques' }); this.predictions = predictions; return predictions; }, makeDecisions() { const decisions = []; if (this.patterns.some(p => p.type === 'Isolated Entities')) decisions.push({ decision: 'Créer relations pour entités isolées', action: 'auto-remediation', priority: 'CRITIQUE', impact: '+15-20 points cohérence', confidence: 0.90 }); if (this.patterns.some(p => p.type === 'Trust Deficit')) decisions.push({ decision: 'Renforcer signaux de confiance (avis, certifications, HTTPS)', action: 'trust-signals', priority: 'HAUTE', impact: '+10-15 points TRUST', confidence: 0.85 }); if (this.patterns.some(p => p.type === 'Authority Gap')) decisions.push({ decision: 'Acquérir backlinks autorité + publications expert', action: 'authority-building', priority: 'HAUTE', impact: '+12-18 points AUTHORITY', confidence: 0.88 }); if (this.patterns.some(p => p.type === 'Negative Sentiment Cluster')) decisions.push({ decision: 'Campagne de gestion de réputation + contenu positif', action: 'reputation-management', priority: 'HAUTE', impact: '+15-20 points SENTIMENT', confidence: 0.82 }); if (this.patterns.some(p => p.type === 'E-E-A-T Weakness')) decisions.push({ decision: 'Publier contenu expert + credentials + bios auteurs', action: 'eeat-content', priority: 'HAUTE', impact: '+15-20 points E-E-A-T', confidence: 0.85 }); if (this.patterns.some(p => p.type === 'Low Influence')) decisions.push({ decision: 'Stratégie netlinking + relations publiques + guest posts', action: 'influence-strategy', priority: 'MOYENNE', impact: '+10-15 points INFLUENCE', confidence: 0.78 }); if (this.patterns.some(p => p.type === 'Competitor Dominance')) decisions.push({ decision: 'Renforcer entités principales vs concurrents', action: 'entity-strengthening', priority: 'MOYENNE', impact: '+8-12 points position', confidence: 0.75 }); if (this.patterns.some(p => p.type === 'Low Graph Density')) decisions.push({ decision: 'Augmenter densité du graphe de connaissances', action: 'graph-density', priority: 'MOYENNE', impact: '+10-15 points cohérence', confidence: 0.80 }); const scoreGap = 500 - state.scores.global; if (scoreGap > 100) decisions.push({ decision: `Plan de récupération: ${scoreGap} points à regagner sur 500`, action: 'plan-global', priority: 'STRATÉGIQUE', impact: `Score cible: ${Math.min(450, state.scores.global + 100)}/500`, confidence: 0.88 }); this.decisions = decisions; return decisions; }, autoRemediate() { if (!state.analysisData) { showToast('Lancez d\'abord la construction du Knowledge Graph', true); return; } addLog('IA: Démarrage auto-remédiation réputationnelle...', 'system'); let actionsTaken = 0; const isolated = state.kgNodes.filter(n => n.isolated); isolated.forEach((entity, i) => { setTimeout(() => { const siblings = state.kgNodes.filter(n => n.type !== 'competitor' && n.id !== entity.id && !n.isolated); if (siblings.length > 0) { const numLinks = Math.min(3, siblings.length); for (let j = 0; j < numLinks; j++) { const sib = siblings[Math.floor(Math.random() * siblings.length)]; const rel = RELATION_TYPES[Math.floor(Math.random() * RELATION_TYPES.length)]; state.kgLinks.push({ from: sib.id, to: entity.id, relType: rel.type, relLabel: rel.label, aiGenerated: true }); } entity.isolated = false; actionsTaken++; this.autoActions++; this.updateAutoCount(); addLog(`IA: Relation créée → ${entity.name} (auto-remédiation)`, 'success'); } }, i * 200); }); const negative = state.kgNodes.filter(n => n.sentiment < 30); negative.forEach((entity, i) => { setTimeout(() => { entity.sentiment = Math.min(100, entity.sentiment + 25); actionsTaken++; this.autoActions++; this.updateAutoCount(); addLog(`IA: Sentiment amélioré → ${entity.name} (+25)`, 'success'); }, (isolated.length + i) * 200 + 500); }); setTimeout(() => { recalculateScores(); updateStats(); renderScoreBreakdown(); renderEntityList(); generateReport(); generateStrategy(); renderRecommendations(); spawnPackets(); this.confidence = Math.min(0.98, 0.7 + (actionsTaken * 0.03)); this.renderStatus(); this.renderMetrics(); addLog(`IA: Auto-remédiation terminée — ${actionsTaken} actions`, 'success'); addLog(`IA: Score réputationnel mis à jour: ${state.scores.global}/500`, 'system'); showToast(`Auto-remédiation: ${actionsTaken} actions ! Score: ${state.scores.global}/500`); }, (isolated.length + negative.length) * 200 + 1000); }, deepLearn() { if (!state.analysisData) { showToast('Lancez d\'abord la construction', true); return; } addLog('IA: Démarrage apprentissage profond réputationnel...', 'system'); if (!this.active) this.initNeural(); for (let epoch = 0; epoch < 5; epoch++) { setTimeout(() => { for (let i = 0; i < this.neural.weights.length; i++) for (let j = 0; j < this.neural.weights[i].length; j++) for (let k = 0; k < this.neural.weights[i][j].length; k++) this.neural.weights[i][j][k] += (Math.random() - 0.5) * this.learningRate; const inputs = [state.scores.trust / 100, state.scores.authority / 100, state.scores.sentiment / 100, state.scores.influence / 100, state.scores.eeat / 100]; const outputs = this.forwardPass(inputs); this.confidence = Math.min(0.99, this.confidence + 0.05); this.iterations++; this.renderStatus(); this.renderLearning(); addLog(`IA: Epoch ${epoch + 1}/5 — Convergence: ${(outputs.reduce((a, b) => a + b, 0) / outputs.length * 100).toFixed(1)}%`, 'info'); if (epoch === 4) { addLog(`IA: Deep Learning terminé — Confiance: ${(this.confidence * 100).toFixed(0)}%`, 'success'); showToast(`Deep Learning terminé ! Confiance: ${(this.confidence * 100).toFixed(0)}%`); } }, epoch * 800); } }, geneticOptimize() { if (!state.analysisData) { showToast('Lancez d\'abord la construction', true); return; } addLog('IA: Démarrage algorithme génétique réputationnel...', 'system'); const popSize = 10; this.geneticPopulation = Array.from({ length: popSize }, () => ({ genes: Array(10).fill(0).map(() => Math.random()), fitness: 0 })); for (let gen = 0; gen < 5; gen++) { setTimeout(() => { this.geneticPopulation.forEach(ind => { const inputs = [state.scores.trust / 100, state.scores.authority / 100, state.scores.sentiment / 100, state.scores.influence / 100, state.scores.eeat / 100]; const outputs = this.forwardPass(inputs); ind.fitness = outputs.reduce((a, b) => a + b, 0) / outputs.length + (Math.random() * 0.1); }); this.geneticPopulation.sort((a, b) => b.fitness - a.fitness); this.bestFitness = this.geneticPopulation[0].fitness; this.geneticGeneration = gen + 1; const elite = this.geneticPopulation.slice(0, popSize / 2); this.geneticPopulation = [...elite, ...elite.map(ind => ({ genes: ind.genes.map(g => g + (Math.random() - 0.5) * 0.1), fitness: 0 }))]; this.renderLearning(); addLog(`IA: Génération ${gen + 1}/5 — Fitness: ${(this.bestFitness * 100).toFixed(1)}%`, 'info'); if (gen === 4) { addLog(`IA: Algo génétique terminé — Fitness: ${(this.bestFitness * 100).toFixed(1)}%`, 'success'); showToast(`Algo génétique terminé ! Fitness: ${(this.bestFitness * 100).toFixed(1)}%`); } }, gen * 1000); } }, runCycle() { if (!state.analysisData) { showToast('Lancez d\'abord la construction', true); return; } addLog('IA: Cycle cognitif complet réputationnel...', 'system'); if (!this.active) this.initNeural(); const inputs = [state.scores.trust / 100, state.scores.authority / 100, state.scores.sentiment / 100, state.scores.influence / 100, state.scores.eeat / 100]; this.forwardPass(inputs); this.recognizePatterns(); this.predictWeaknesses(); this.makeDecisions(); this.iterations++; this.confidence = Math.min(0.95, 0.6 + this.iterations * 0.02); this.renderStatus(); this.renderDecisions(); this.renderMetrics(); this.renderLearning(); addLog(`IA: Cycle #${this.iterations} — ${this.patterns.length} patterns, ${this.predictions.length} prédictions, ${this.decisions.length} décisions`, 'success'); showToast(`Cycle cognitif #${this.iterations} terminé !`); }, activate() { if (this.active) { this.deactivate(); return; } if (!state.analysisData) { showToast('Lancez d\'abord la construction', true); return; } this.active = true; this.initNeural(); this.renderStatus(); this.startNeuralAnimation(); this.runCycle(); const label = document.getElementById('aiActivateLabel'); if (label) label.textContent = 'Désactiver l\'IA'; addLog('IA: Moteur cognitif réputationnel activé — Neural Reputation Engine v3.0', 'system'); showToast('Moteur cognitif IA activé ! 🧠'); }, deactivate() { this.active = false; this.automation = false; this.stopAutomation(); this.stopNeuralAnimation(); const toggle = document.getElementById('aiAutomationToggle'); if (toggle) toggle.checked = false; this.renderStatus(); this.renderToggle(); const label = document.getElementById('aiActivateLabel'); if (label) label.textContent = 'Activer l\'IA'; addLog('IA: Moteur cognitif désactivé', 'system'); showToast('Moteur cognitif IA désactivé'); }, toggleAutomation() { const toggle = document.getElementById('aiAutomationToggle'); const isChecked = toggle ? toggle.checked : false; if (!this.active) { if (toggle) toggle.checked = false; this.renderToggle(); showToast('Activez d\'abord l\'IA', true); return; } this.automation = isChecked; if (this.automation) { this.startAutomation(); addLog('IA: Automatisation activée', 'system'); showToast('Automatisation IA activée ! ⚙️'); } else { this.stopAutomation(); addLog('IA: Automatisation désactivée', 'system'); showToast('Automatisation IA désactivée'); } this.renderToggle(); }, startAutomation() { this.automationInterval = setInterval(() => { if (this.active && this.automation) { this.runCycle(); if (this.patterns.some(p => p.severity === 'critical') && Math.random() > 0.5) { addLog('IA: Pattern critique — auto-remédiation', 'warning'); this.autoRemediate(); } } }, 5000); }, stopAutomation() { if (this.automationInterval) { clearInterval(this.automationInterval); this.automationInterval = null; } }, startNeuralAnimation() { const canvas = document.getElementById('neuralCanvas'); if (!canvas) return; function resize() { const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width - 8; canvas.height = 220; } resize(); window.addEventListener('resize', resize); const animate = () => { if (!this.active) { this.neuralAnimId = null; return; } const ctx = canvas.getContext('2d'), w = canvas.width, h = canvas.height; ctx.clearRect(0, 0, w, h); const lc = this.neural.layers.length, ls = w / (lc + 1); const np = this.neural.layers.map((count, li) => { const pos = [], ns = h / (count + 1); for (let i = 0; i < count; i++) pos.push({ x: ls * (li + 1), y: ns * (i + 1) }); return pos; }); for (let i = 0; i < this.neural.weights.length; i++) for (let j = 0; j < this.neural.weights[i].length; j++) for (let k = 0; k < this.neural.weights[i][j].length; k++) { const f = np[i][k], t = np[i + 1][j], wt = this.neural.weights[i][j][k]; ctx.beginPath(); ctx.moveTo(f.x, f.y); ctx.lineTo(t.x, t.y); ctx.strokeStyle = wt > 0 ? `rgba(139,92,246,${Math.abs(wt) * 0.15})` : `rgba(99,102,241,${Math.abs(wt) * 0.15})`; ctx.lineWidth = Math.abs(wt) * 1.5; ctx.stroke(); } this.neural.pulses = this.neural.pulses.filter(p => { p.progress += p.speed; if (p.progress >= 1) return false; const f = np[p.layer][p.fromNeuron], t = np[p.layer + 1][p.toNeuron]; const px = f.x + (t.x - f.x) * p.progress, py = f.y + (t.y - f.y) * p.progress; ctx.beginPath(); ctx.arc(px, py, 4, 0, Math.PI * 2); ctx.fillStyle = `rgba(139,92,246,${p.intensity * 0.4})`; ctx.fill(); ctx.beginPath(); ctx.arc(px, py, 2, 0, Math.PI * 2); ctx.fillStyle = '#a78bfa'; ctx.fill(); return true; }); for (let i = 0; i < np.length; i++) for (let j = 0; j < np[i].length; j++) { const pos = np[i][j], act = this.neural.activations[i][j] || 0, r = i === 0 || i === np.length - 1 ? 7 : 6; const gr = r + 6 + act * 4; const grad = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, gr); grad.addColorStop(0, `rgba(139,92,246,${act * 0.4})`); grad.addColorStop(1, 'rgba(139,92,246,0)'); ctx.beginPath(); ctx.arc(pos.x, pos.y, gr, 0, Math.PI * 2); ctx.fillStyle = grad; ctx.fill(); ctx.beginPath(); ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2); ctx.fillStyle = '#1e293b'; ctx.strokeStyle = act > 0.5 ? '#a78bfa' : act > 0.2 ? '#8b5cf6' : '#6366f1'; ctx.lineWidth = 2; ctx.fill(); ctx.stroke(); if (act > 0.1) { ctx.beginPath(); ctx.arc(pos.x, pos.y, 2.5, 0, Math.PI * 2); ctx.fillStyle = `rgba(167,139,250,${act})`; ctx.fill(); } } const labels = ['INPUT', 'HIDDEN', 'HIDDEN', 'HIDDEN', 'OUTPUT']; ctx.font = '8px JetBrains Mono'; ctx.textAlign = 'center'; for (let i = 0; i < lc; i++) { ctx.fillStyle = '#475569'; ctx.fillText(labels[i] || `L${i}`, ls * (i + 1), h - 5); } if (Math.random() < 0.3 && this.neural.pulses.length < 30) { const layer = Math.floor(Math.random() * (lc - 1)); this.neural.pulses.push({ layer, fromNeuron: Math.floor(Math.random() * this.neural.layers[layer]), toNeuron: Math.floor(Math.random() * this.neural.layers[layer + 1]), progress: 0, speed: 0.03 + Math.random() * 0.02, intensity: 0.5 + Math.random() * 0.5 }); } this.neuralAnimId = requestAnimationFrame(animate); }; animate(); }, stopNeuralAnimation() { if (this.neuralAnimId) { cancelAnimationFrame(this.neuralAnimId); this.neuralAnimId = null; } const canvas = document.getElementById('neuralCanvas'); if (canvas) { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); } }, updateAutoCount() { const el = document.getElementById('aiAutoCount'); if (el) el.textContent = this.autoActions; }, renderStatus() { const dot = document.getElementById('aiStatusDot'), text = document.getElementById('aiStatusText'); const confEl = document.getElementById('aiConfidence'), iterEl = document.getElementById('aiIterations'); if (this.active) { if (dot) dot.className = 'w-2 h-2 bg-green-400 rounded-full animate-pulse'; if (text) { text.textContent = 'ACTIVE'; text.className = 'text-xs font-mono text-green-400'; } } else { if (dot) dot.className = 'w-2 h-2 bg-slate-600 rounded-full'; if (text) { text.textContent = 'DORMANT'; text.className = 'text-xs font-mono text-slate-500'; } } if (confEl) confEl.textContent = `${Math.round(this.confidence * 100)}%`; if (iterEl) iterEl.textContent = this.iterations; this.updateAutoCount(); }, renderToggle() { const bg = document.getElementById('automationToggleBg'), knob = document.getElementById('automationToggleKnob'); if (this.automation) { if (bg) bg.className = 'w-10 h-5 bg-violet-500 rounded-full transition-colors'; if (knob) knob.className = 'absolute left-5 top-0.5 w-4 h-4 bg-white rounded-full transition-all'; } else { if (bg) bg.className = 'w-10 h-5 bg-slate-800 rounded-full transition-colors'; if (knob) knob.className = 'absolute left-0.5 top-0.5 w-4 h-4 bg-slate-600 rounded-full transition-all'; } }, renderDecisions() { const list = document.getElementById('aiDecisionsList'); if (!list) return; if (this.decisions.length === 0) { list.innerHTML = '
Aucune décision IA
'; return; } const pc = { 'CRITIQUE': 'text-red-400 bg-red-500/10 border-red-500/20', 'HAUTE': 'text-amber-400 bg-amber-500/10 border-amber-500/20', 'MOYENNE': 'text-cyan-400 bg-cyan-500/10 border-cyan-500/20', 'STRATÉGIQUE': 'text-violet-400 bg-violet-500/10 border-violet-500/20' }; list.innerHTML = this.decisions.map((d, i) => { const c = pc[d.priority] || pc['MOYENNE'], [tc, bc, bd] = c.split(' '); return `
${d.decision}${d.priority}
Impact: ${d.impact} · Confiance: ${(d.confidence * 100).toFixed(0)}%
`; }).join(''); lucide.createIcons(); }, renderMetrics() { const pe = document.getElementById('aiPatterns'), pre = document.getElementById('aiPredictions'); if (pe) { if (this.patterns.length > 0) pe.innerHTML = this.patterns.map(p => `
${p.type}${(p.confidence * 100).toFixed(0)}%
`).join(''); else pe.textContent = '—'; } if (pre) { if (this.predictions.length > 0) pre.innerHTML = this.predictions.map(p => `
${p.prediction}
Prob: ${(p.probability * 100).toFixed(0)}% · ${p.timeframe}
`).join(''); else pre.textContent = '—'; } }, renderLearning() { const el = document.getElementById('aiLearning'); if (!el) return; if (this.geneticGeneration > 0) el.innerHTML = `
Génération: ${this.geneticGeneration}
Fitness: ${(this.bestFitness * 100).toFixed(1)}%
`; else if (this.iterations > 0) el.innerHTML = `
Itérations: ${this.iterations}
Confiance: ${(this.confidence * 100).toFixed(0)}%
`; else el.textContent = '—'; }, }; // ---- Init ---- document.addEventListener('DOMContentLoaded', () => { lucide.createIcons(); startClock(); initCanvas(); CognitiveAI.renderStatus(); CognitiveAI.renderToggle(); addLog('Knowledge Graph Reputationnel v3.0 initialisé', 'system'); addLog('Dimensions: TRUST · AUTHORITY · SENTIMENT · INFLUENCE · E-E-A-T', 'info'); addLog('IA: Neural Reputation Engine v3.0 — en attente d\'activation', 'system'); addLog('En attente de configuration...', 'info'); document.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { const input = document.getElementById('domainInput'); if (input && document.activeElement === input) { e.preventDefault(); buildKnowledgeGraph(); } } }); }); function startClock() { function update() { const now = new Date(); const el = document.getElementById('systemTime'); if (el) el.textContent = now.toUTCString().split(' ')[4] + ' UTC'; } update(); setInterval(update, 1000); } // ---- Build Knowledge Graph ---- function buildKnowledgeGraph() { const di = document.getElementById('domainInput'), ei = document.getElementById('entitiesInput'), ki = document.getElementById('keywordsInput'), ci = document.getElementById('competitorsInput'); if (!di || !di.value.trim()) { showToast('Entrez un domaine/marque cible', true); return; } if (state.analyzing) { showToast('Construction déjà en cours'); return; } state.domain = di.value.trim(); state.entities = ei && ei.value.trim() ? ei.value.trim().split('\n').map(s => s.trim()).filter(s => s) : []; state.keywords = ki && ki.value.trim() ? ki.value.trim().split('\n').map(s => s.trim()).filter(s => s) : ['expert', 'professionnel', 'service', 'qualité']; state.competitors = ci && ci.value.trim() ? ci.value.trim().split('\n').map(s => s.trim()).filter(s => s) : []; state.dimensions = Array.from(document.querySelectorAll('.dim-cb:checked')).map(cb => cb.value); if (state.dimensions.length === 0) state.dimensions = ['trust', 'authority', 'sentiment', 'influence', 'eeat']; state.analyzing = true; addLog(`Construction du Knowledge Graph pour ${state.domain}...`, 'system'); addLog(`Entités: ${state.entities.length} · Mots-clés: ${state.keywords.length} · Concurrents: ${state.competitors.length}`, 'info'); const procDiv = document.getElementById('processing'), stagesEl = document.getElementById('stages'), barEl = document.getElementById('progressBar'); if (procDiv) procDiv.classList.remove('hidden'); const stages = [ { msg: '→ Extraction des entités du domaine cible...', dur: 500 }, { msg: '→ Identification des types d\'entités (marque, personne, org)...', dur: 500 }, { msg: '→ Génération des relations inter-entités...', dur: 500 }, { msg: '→ Calcul des scores TRUST et AUTHORITY...', dur: 500 }, { msg: '→ Analyse SENTIMENT et INFLUENCE...', dur: 500 }, { msg: '→ Évaluation E-E-A-T (Expertise, Experience, Authority, Trust)...', dur: 500 }, { msg: '→ Détection des entités isolées et clusters négatifs...', dur: 400 }, { msg: '→ Optimisation cognitive IA du graphe...', dur: 400 }, ]; let progress = 0, stageIndex = 0; if (stagesEl) stagesEl.innerHTML = ''; function processStage() { if (stageIndex >= stages.length) { state.analyzing = false; if (procDiv) procDiv.classList.add('hidden'); generateKGResults(); return; } const stage = stages[stageIndex]; addLog(stage.msg.replace('→ ', ''), 'info'); if (stagesEl) { const sd = document.createElement('div'); sd.className = 'flex items-center gap-2'; sd.innerHTML = `${stage.msg}`; stagesEl.appendChild(sd); lucide.createIcons(); } progress += (100 / stages.length); if (barEl) barEl.style.width = `${progress}%`; setTimeout(() => { stageIndex++; processStage(); }, stage.dur); } processStage(); } function generateKGResults() { const domain = state.domain, entities = state.entities, keywords = state.keywords, competitors = state.competitors; state.kgNodes = []; state.kgLinks = []; let nodeId = 0; // Primary entity (domain) const primaryNode = { id: nodeId++, name: domain, type: 'brand', label: 'Marque Principale', trust: 60 + Math.floor(Math.random() * 30), authority: 55 + Math.floor(Math.random() * 30), sentiment: 50 + Math.floor(Math.random() * 35), influence: 45 + Math.floor(Math.random() * 35), eeat: 50 + Math.floor(Math.random() * 30), isolated: false, x: 0.5, y: 0.5, level: 0, }; state.kgNodes.push(primaryNode); // User-defined entities entities.forEach((entity, i) => { const et = ENTITY_TYPES[Math.floor(Math.random() * 4)]; // brand, person, org, concept const angle = (i / Math.max(entities.length, 1)) * Math.PI * 2; const radius = 0.25; const node = { id: nodeId++, name: entity, type: et.type, label: et.label, trust: 30 + Math.floor(Math.random() * 60), authority: 25 + Math.floor(Math.random() * 60), sentiment: 20 + Math.floor(Math.random() * 70), influence: 20 + Math.floor(Math.random() * 60), eeat: 25 + Math.floor(Math.random() * 60), isolated: Math.random() > 0.6, x: 0.5 + Math.cos(angle) * radius, y: 0.5 + Math.sin(angle) * radius * 0.8, level: 1, }; state.kgNodes.push(node); if (!node.isolated) { const rel = RELATION_TYPES[Math.floor(Math.random() * RELATION_TYPES.length)]; state.kgLinks.push({ from: primaryNode.id, to: node.id, relType: rel.type, relLabel: rel.label, color: rel.color }); } }); // Competitor entities competitors.forEach((comp, i) => { const angle = (i / Math.max(competitors.length, 1)) * Math.PI * 2 + Math.PI; const radius = 0.35; const node = { id: nodeId++, name: comp, type: 'competitor', label: 'Concurrent', trust: 20 + Math.floor(Math.random() * 50), authority: 30 + Math.floor(Math.random() * 50), sentiment: 15 + Math.floor(Math.random() * 50), influence: 25 + Math.floor(Math.random() * 50), eeat: 20 + Math.floor(Math.random() * 50), isolated: false, x: 0.5 + Math.cos(angle) * radius, y: 0.5 + Math.sin(angle) * radius * 0.8, level: 2, }; state.kgNodes.push(node); state.kgLinks.push({ from: primaryNode.id, to: node.id, relType: 'competes-with', relLabel: 'concurrence', color: '#ef4444' }); }); // Keyword/concept entities keywords.forEach((kw, i) => { const angle = (i / Math.max(keywords.length, 1)) * Math.PI * 2 + Math.PI / 2; const radius = 0.2; const node = { id: nodeId++, name: kw, type: 'concept', label: 'Concept', trust: 40 + Math.floor(Math.random() * 40), authority: 35 + Math.floor(Math.random() * 40), sentiment: 40 + Math.floor(Math.random() * 50), influence: 30 + Math.floor(Math.random() * 40), eeat: 35 + Math.floor(Math.random() * 45), isolated: Math.random() > 0.7, x: 0.5 + Math.cos(angle) * radius, y: 0.5 + Math.sin(angle) * radius * 0.7, level: 1, }; state.kgNodes.push(node); if (!node.isolated) { const rel = RELATION_TYPES[Math.floor(Math.random() * RELATION_TYPES.length)]; state.kgLinks.push({ from: primaryNode.id, to: node.id, relType: rel.type, relLabel: rel.label, color: rel.color }); } }); // Generate inter-entity relationships const nonCompetitors = state.kgNodes.filter(n => n.type !== 'competitor' && n.type !== 'brand'); nonCompetitors.forEach((node, i) => { if (Math.random() > 0.5) { const other = nonCompetitors[Math.floor(Math.random() * nonCompetitors.length)]; if (other.id !== node.id) { const rel = RELATION_TYPES[Math.floor(Math.random() * RELATION_TYPES.length)]; state.kgLinks.push({ from: node.id, to: other.id, relType: rel.type, relLabel: rel.label, color: rel.color }); } } }); // Calculate scores recalculateScores(); state.analysisData = { domain, entities: state.kgNodes.length, relations: state.kgLinks.length, isolated: state.kgNodes.filter(n => n.isolated).length, dimensions: state.dimensions }; const ph = document.getElementById('kgPlaceholder'); if (ph) ph.classList.add('hidden'); updateStats(); renderScoreBreakdown(); renderEntityList(); generateReport(); generateStrategy(); renderRecommendations(); spawnPackets(); animateKG(); addLog(`Knowledge Graph construit: ${state.kgNodes.length} entités, ${state.kgLinks.length} relations`, 'success'); addLog(`Scores: TRUST=${state.scores.trust} AUTH=${state.scores.authority} SENT=${state.scores.sentiment} INFL=${state.scores.influence} E-E-A-T=${state.scores.eeat} Global=${state.scores.global}/500`, 'info'); addLog(`${state.kgNodes.filter(n => n.isolated).length} entité(s) isolée(s) détectée(s)`, 'warning'); CognitiveAI.renderStatus(); addLog('IA: Données reçues — moteur cognitif prêt', 'system'); showToast(`Knowledge Graph construit ! ${state.kgNodes.length} entités · Score: ${state.scores.global}/500`); } function recalculateScores() { const total = state.kgNodes.length; const nonIsolated = state.kgNodes.filter(n => !n.isolated); const avgTrust = nonIsolated.length > 0 ? nonIsolated.reduce((a, n) => a + n.trust, 0) / nonIsolated.length : 0; const avgAuthority = nonIsolated.length > 0 ? nonIsolated.reduce((a, n) => a + n.authority, 0) / nonIsolated.length : 0; const avgSentiment = nonIsolated.length > 0 ? nonIsolated.reduce((a, n) => a + n.sentiment, 0) / nonIsolated.length : 0; const avgInfluence = nonIsolated.length > 0 ? nonIsolated.reduce((a, n) => a + n.influence, 0) / nonIsolated.length : 0; const avgEEAT = nonIsolated.length > 0 ? nonIsolated.reduce((a, n) => a + n.eeat, 0) / nonIsolated.length : 0; const connectedRatio = nonIsolated.length / Math.max(total, 1); const linkDensity = state.kgLinks.length / Math.max(total, 1); state.scores.trust = Math.round(avgTrust * 0.7 + connectedRatio * 30); state.scores.authority = Math.round(avgAuthority * 0.6 + Math.min(linkDensity * 10, 40)); state.scores.sentiment = Math.round(avgSentiment * 0.8 + connectedRatio * 20); state.scores.influence = Math.round(avgInfluence * 0.6 + Math.min(linkDensity * 10, 40)); state.scores.eeat = Math.round(avgEEAT * 0.7 + connectedRatio * 30); state.scores.global = state.scores.trust + state.scores.authority + state.scores.sentiment + state.scores.influence + state.scores.eeat; state.scoreBreakdown = { connectivity: Math.round(connectedRatio * 100), density: Math.round(Math.min(linkDensity * 50, 100)), entities: total, isolated: state.kgNodes.filter(n => n.isolated).length }; } function updateStats() { animateCounter(document.getElementById('statTrust'), state.scores.trust); animateCounter(document.getElementById('statAuthority'), state.scores.authority); animateCounter(document.getElementById('statSentiment'), state.scores.sentiment); animateCounter(document.getElementById('statInfluence'), state.scores.influence); animateCounter(document.getElementById('statEEAT'), state.scores.eeat); const ecEl = document.getElementById('entityCount'); if (ecEl) ecEl.textContent = state.kgNodes.length; const rcEl = document.getElementById('relationCount'); if (rcEl) rcEl.textContent = state.kgLinks.length; const rsEl = document.getElementById('repScore'); if (rsEl) rsEl.textContent = state.scores.global; const ring = document.getElementById('scoreRing'), numEl = document.getElementById('scoreNumber'); if (ring) { const c = 2 * Math.PI * 50; ring.style.strokeDashoffset = c - (state.scores.global / 500) * c; } if (numEl) animateCounter(numEl, state.scores.global, 1000); } function animateCounter(el, target, duration = 800) { if (!el) return; const start = parseInt(el.textContent) || 0, startTime = performance.now(); function step(ct) { const e = ct - startTime, p = Math.min(e / duration, 1), eased = 1 - Math.pow(1 - p, 3); el.textContent = Math.floor(start + (target - start) * eased).toLocaleString(); if (p < 1) requestAnimationFrame(step); } requestAnimationFrame(step); } function renderScoreBreakdown() { const c = document.getElementById('scoreDimensions'); if (!c) return; const dims = [ { name: 'TRUST — Confiance', value: state.scores.trust, max: 100, color: '#8b5cf6', icon: 'shield-check', desc: 'Avis, certifications, HTTPS, transparence' }, { name: 'AUTHORITY — Autorité', value: state.scores.authority, max: 100, color: '#06b6d4', icon: 'award', desc: 'Backlinks autorité, publications expert' }, { name: 'SENTIMENT — Sentiment', value: state.scores.sentiment, max: 100, color: '#a855f7', icon: 'smile', desc: 'Tone positif, mentions favorables' }, { name: 'INFLUENCE — Influence', value: state.scores.influence, max: 100, color: '#f59e0b', icon: 'trending-up', desc: 'Portée réseau, citations, partages' }, { name: 'E-E-A-T — Expertise', value: state.scores.eeat, max: 100, color: '#22c55e', icon: 'badge-check', desc: 'Expertise, Expérience, Autorité, Trust' }, ]; c.innerHTML = dims.map(d => `
${d.name}
${d.value}/${d.max}
${d.desc}
`).join(''); lucide.createIcons(); } function renderEntityList() { const list = document.getElementById('entityList'), countEl = document.getElementById('entityListCount'); if (!list) return; if (countEl) countEl.textContent = state.kgNodes.length; if (state.kgNodes.length === 0) { list.innerHTML = '
Aucune entité analysée
'; return; } const typeIcons = { brand: 'building-2', person: 'user', organization: 'briefcase', concept: 'lightbulb', product: 'package', location: 'map-pin', competitor: 'swords', influencer: 'star' }; list.innerHTML = state.kgNodes.map((n, i) => { const repScore = Math.round((n.trust + n.authority + n.sentiment + n.influence + n.eeat) / 5); const repColor = repScore > 60 ? 'text-green-400' : repScore > 40 ? 'text-amber-400' : 'text-red-400'; const sentIcon = n.sentiment > 60 ? 'smile' : n.sentiment > 40 ? 'meh' : 'frown'; const sentColor = n.sentiment > 60 ? 'text-green-400' : n.sentiment > 40 ? 'text-amber-400' : 'text-red-400'; return `
${n.name}${n.label}${n.isolated ? 'isolé' : ''}
T:${n.trust} A:${n.authority} ${n.sentiment} I:${n.influence} E:${n.eeat}
${repScore}
`; }).join(''); lucide.createIcons(); } function generateReport() { const o = document.getElementById('reportOutput'); if (!o || !state.analysisData) return; const r = state.analysisData; let c = `╔══════════════════════════════════════════════════════════╗\n║ RAPPORT KNOWLEDGE GRAPH REPUTATIONNEL (IA v3.0) ║\n╚══════════════════════════════════════════════════════════╝\n\n`; c += `Domaine/Marque: ${r.domain}\nDate: ${new Date().toISOString()}\nDimensions: ${r.dimensions.join(', ').toUpperCase()}\n${'─'.repeat(60)}\n\n`; c += `=== SCORE RÉPUTATIONNEL GLOBAL ===\n\n Score Global: ${state.scores.global}/500\n\n TRUST: ${state.scores.trust}/100\n AUTHORITY: ${state.scores.authority}/100\n SENTIMENT: ${state.scores.sentiment}/100\n INFLUENCE: ${state.scores.influence}/100\n E-E-A-T: ${state.scores.eeat}/100\n\n`; c += `=== STRUCTURE DU GRAPHE ===\n\n Entités totales: ${r.entities}\n Relations: ${r.relations}\n Entités isolées: ${r.isolated}\n Connectivité: ${state.scoreBreakdown.connectivity}%\n Densité: ${state.scoreBreakdown.density}%\n\n`; c += `=== ENTITÉS ANALYSÉES ===\n\n`; state.kgNodes.forEach((n, i) => { const repScore = Math.round((n.trust + n.authority + n.sentiment + n.influence + n.eeat) / 5); c += ` [${i + 1}] ${n.name} (${n.label})\n`; c += ` T=${n.trust} A=${n.authority} S=${n.sentiment} I=${n.influence} E=${n.eeat} → ${repScore}/100\n`; c += ` ${n.isolated ? '⚠ ISOLÉE' : '✓ Connectée'}\n\n`; }); c += `=== RELATIONS DÉTECTÉES ===\n\n`; state.kgLinks.slice(0, 20).forEach((l, i) => { const from = state.kgNodes.find(n => n.id === l.from), to = state.kgNodes.find(n => n.id === l.to); if (from && to) c += ` [${i + 1}] ${from.name} —${l.relLabel}→ ${to.name}\n`; }); if (state.kgLinks.length > 20) c += `\n ... et ${state.kgLinks.length - 20} autres relations\n`; c += `\n${'─'.repeat(60)}\nGénéré par SentinelAI Knowledge Graph Reputationnel v3.0\n`; o.textContent = c; } function generateStrategy() { const o = document.getElementById('strategyOutput'); if (!o || !state.analysisData) return; let c = `╔══════════════════════════════════════════════════════════╗\n║ STRATÉGIE DE RENFORCEMENT RÉPUTATIONNEL (IA v3.0) ║\n╚══════════════════════════════════════════════════════════╝\n\n`; c += `Domaine: ${state.domain}\nScore global: ${state.scores.global}/500\n${'─'.repeat(60)}\n\n`; c += `=== PHASE 1 — RENFORCER TRUST ===\n\n 1. Collecter et afficher avis clients vérifiés\n 2. Afficher certifications et labels de confiance\n 3. Sécuriser: HTTPS, privacy policy, mentions légales\n 4. Transparence: prix, CGV, contact visible\n\n`; c += `=== PHASE 2 — DÉVELOPPER AUTHORITY ===\n\n 1. Acquérir backlinks de sites autorité (DA>50)\n 2. Publier contenu expert sur blogs/médias\n 3. Participer à conférences, podcasts, interviews\n 4. Obtenir mentions dans presse spécialisée\n\n`; c += `=== PHASE 3 — AMÉLIORER SENTIMENT ===\n\n 1. Répondre aux avis négatifs (tous supports)\n 2. Encourager avis positifs post-achat\n 3. Surveiller mentions sur réseaux sociaux\n 4. Créer contenu positif: témoignages, case studies\n\n`; c += `=== PHASE 4 — AUGMENTER INFLUENCE ===\n\n 1. Stratégie netlinking ciblée\n 2. Guest posts sur blogs influents\n 3. Relations publiques + partenariats\n 4. Créer contenu shareable (infographies, études)\n\n`; c += `=== PHASE 5 — OPTIMISER E-E-A-T ===\n\n 1. Pages auteurs avec bio + credentials + photo\n 2. Schema.org: Author, Organization, Expert\n 3. Publier contenu expert: guides, études, recherches\n 4. Obtenir mentions de médias reconnus\n 5. Dates de mise à jour visibles sur contenu\n\n`; c += `=== PHASE 6 — DENSIFIER LE GRAPHE ===\n\n 1. Connecter entités isolées (${state.kgNodes.filter(n => n.isolated).length} détectées)\n 2. Créer relations inter-entités\n 3. Ajouter co-occurrences dans le contenu\n 4. Structurer données avec schema.org (Entity relations)\n\n`; c += `=== ACTIONS PRIORITAIRES ===\n\n`; const isolated = state.kgNodes.filter(n => n.isolated); if (isolated.length > 0) { c += ` [CRITIQUE] Connecter ${isolated.length} entité(s) isolée(s)\n`; } if (state.scores.trust < 50) { c += ` [HAUTE] Renforcer TRUST (${state.scores.trust}/100)\n`; } if (state.scores.sentiment < 50) { c += ` [HAUTE] Améliorer SENTIMENT (${state.scores.sentiment}/100)\n`; } if (state.scores.eeat < 50) { c += ` [HAUTE] Optimiser E-E-A-T (${state.scores.eeat}/100)\n`; } if (state.scores.authority < 50) { c += ` [HAUTE] Développer AUTHORITY (${state.scores.authority}/100)\n`; } if (state.scores.influence < 50) { c += ` [MOYENNE] Augmenter INFLUENCE (${state.scores.influence}/100)\n`; } c += `\n${'─'.repeat(60)}\nGénéré par SentinelAI Knowledge Graph Reputationnel v3.0\n`; o.textContent = c; } function renderRecommendations() { const c = document.getElementById('recommendationsContent'); if (!c) return; const recs = [ { icon: 'link', color: 'violet', title: 'Connecter les entités isolées', desc: `${state.kgNodes.filter(n => n.isolated).length} entité(s) isolée(s). Créer des relations pour densifier le graphe.`, priority: state.kgNodes.filter(n => n.isolated).length > 0 ? 'Haute' : 'Faible' }, { icon: 'shield-check', color: 'violet', title: 'Renforcer les signaux de confiance', desc: `Score TRUST: ${state.scores.trust}/100. Avis, certifications, HTTPS, transparence.`, priority: state.scores.trust < 60 ? 'Haute' : 'Moyenne' }, { icon: 'award', color: 'cyan', title: 'Développer l\'autorité', desc: `Score AUTHORITY: ${state.scores.authority}/100. Backlinks autorité, publications expert.`, priority: state.scores.authority < 60 ? 'Haute' : 'Moyenne' }, { icon: 'smile', color: 'purple', title: 'Améliorer le sentiment', desc: `Score SENTIMENT: ${state.scores.sentiment}/100. Gérer avis négatifs, encourager positifs.`, priority: state.scores.sentiment < 60 ? 'Haute' : 'Moyenne' }, { icon: 'trending-up', color: 'amber', title: 'Augmenter l\'influence', desc: `Score INFLUENCE: ${state.scores.influence}/100. Netlinking, guest posts, relations publiques.`, priority: state.scores.influence < 50 ? 'Haute' : 'Moyenne' }, { icon: 'badge-check', color: 'green', title: 'Optimiser E-E-A-T', desc: `Score E-E-A-T: ${state.scores.eeat}/100. Pages auteurs, schema.org, contenu expert.`, priority: state.scores.eeat < 60 ? 'Haute' : 'Moyenne' }, { icon: 'share-2', color: 'violet', title: 'Densifier le graphe de connaissances', desc: `Densité: ${state.scoreBreakdown.density}%. Créer plus de relations inter-entités.`, priority: state.scoreBreakdown.density < 60 ? 'Haute' : 'Faible' }, { icon: 'swords', color: 'red', title: 'Surveiller la concurrence', desc: `${state.kgNodes.filter(n => n.type === 'competitor').length} concurrent(s) dans le graphe. Analyser leur réputation.`, priority: 'Moyenne' }, ]; const pc = { 'Haute': 'text-red-400 bg-red-500/10 border-red-500/20', 'Moyenne': 'text-amber-400 bg-amber-500/10 border-amber-500/20', 'Faible': 'text-green-400 bg-green-500/10 border-green-500/20' }; const cc = { violet: 'text-violet-400 bg-violet-500/10', cyan: 'text-cyan-400 bg-cyan-500/10', purple: 'text-purple-400 bg-purple-500/10', amber: 'text-amber-400 bg-amber-500/10', green: 'text-green-400 bg-green-500/10', red: 'text-red-400 bg-red-500/10' }; c.innerHTML = recs.map((r, i) => { const p = pc[r.priority] || pc['Moyenne'], [ptc, pbc, pbd] = p.split(' '); const col = cc[r.color] || cc.violet, [tc, bc] = col.split(' '); return `
${r.title}${r.priority}

${r.desc}

`; }).join(''); lucide.createIcons(); } // ---- Canvas ---- function initCanvas() { const canvas = document.getElementById('kgCanvas'); if (!canvas) return; function resize() { const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width - 16; canvas.height = 480; } resize(); window.addEventListener('resize', resize); canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(), mx = e.clientX - rect.left, my = e.clientY - rect.top; state.hoveredNode = null; for (const node of state.kgNodes) { const nx = node.x * canvas.width, ny = node.y * canvas.height; const r = node.type === 'brand' ? 18 : 12; if (Math.sqrt((mx - nx) ** 2 + (my - ny) ** 2) < r + 4) { state.hoveredNode = node; break; } } const tt = document.getElementById('kgTooltip'); if (state.hoveredNode) { canvas.style.cursor = 'pointer'; if (tt) { tt.classList.remove('hidden'); tt.style.left = (mx + 12) + 'px'; tt.style.top = (my - 40) + 'px'; const repScore = Math.round((state.hoveredNode.trust + state.hoveredNode.authority + state.hoveredNode.sentiment + state.hoveredNode.influence + state.hoveredNode.eeat) / 5); const repColor = repScore > 60 ? 'text-green-400' : repScore > 40 ? 'text-amber-400' : 'text-red-400'; tt.innerHTML = `
${state.hoveredNode.name}
${state.hoveredNode.label}
T:${state.hoveredNode.trust} A:${state.hoveredNode.authority}
S:${state.hoveredNode.sentiment} I:${state.hoveredNode.influence}
E:${state.hoveredNode.eeat}
Réputation: ${repScore}/100
${state.hoveredNode.isolated ? '
ISOLÉE
' : ''}`; } } else { canvas.style.cursor = 'grab'; if (tt) tt.classList.add('hidden'); } }); canvas.addEventListener('mouseleave', () => { state.hoveredNode = null; const tt = document.getElementById('kgTooltip'); if (tt) tt.classList.add('hidden'); }); } function spawnPackets() { state.packets = []; if (state.kgLinks.length === 0) return; for (let i = 0; i < Math.min(state.kgLinks.length, 25); i++) spawnPacket(); } function spawnPacket() { if (state.kgLinks.length === 0 || state.packets.length > 35) return; const link = state.kgLinks[Math.floor(Math.random() * state.kgLinks.length)]; const f = state.kgNodes.find(n => n.id === link.from), t = state.kgNodes.find(n => n.id === link.to); if (!f || !t) return; state.packets.push({ fromId: link.from, toId: link.to, progress: 0, speed: Math.random() * 0.012 + 0.006, color: link.color || '#a78bfa' }); } function animateKG() { const canvas = document.getElementById('kgCanvas'); if (!canvas) { state.canvasAnimId = requestAnimationFrame(animateKG); return; } const ctx = canvas.getContext('2d'), w = canvas.width, h = canvas.height; ctx.clearRect(0, 0, w, h); // Draw links state.kgLinks.forEach(link => { const f = state.kgNodes.find(n => n.id === link.from), t = state.kgNodes.find(n => n.id === link.to); if (!f || !t) return; ctx.beginPath(); ctx.moveTo(f.x * w, f.y * h); ctx.lineTo(t.x * w, t.y * h); ctx.strokeStyle = (link.color || '#8b5cf6') + '30'; ctx.lineWidth = 1; ctx.stroke(); }); // Draw packets state.packets = state.packets.filter(p => { p.progress += p.speed; if (p.progress >= 1) return false; const f = state.kgNodes.find(n => n.id === p.fromId), t = state.kgNodes.find(n => n.id === p.toId); if (!f || !t) return false; const px = f.x * w + (t.x * w - f.x * w) * p.progress, py = f.y * h + (t.y * h - f.y * h) * p.progress; ctx.beginPath(); ctx.arc(px, py, 4, 0, Math.PI * 2); ctx.fillStyle = p.color + '40'; ctx.fill(); ctx.beginPath(); ctx.arc(px, py, 2, 0, Math.PI * 2); ctx.fillStyle = p.color; ctx.fill(); return true; }); // Draw nodes state.kgNodes.forEach(node => { const nx = node.x * w, ny = node.y * h, isH = state.hoveredNode === node; const r = node.type === 'brand' ? 18 : 12; const repScore = Math.round((node.trust + node.authority + node.sentiment + node.influence + node.eeat) / 5); const nodeColor = node.isolated ? '#64748b' : node.type === 'competitor' ? '#ef4444' : repScore > 60 ? '#22c55e' : repScore > 40 ? '#06b6d4' : '#8b5cf6'; // Glow const grad = ctx.createRadialGradient(nx, ny, 0, nx, ny, r + 12); grad.addColorStop(0, nodeColor + '40'); grad.addColorStop(1, 'rgba(0,0,0,0)'); ctx.beginPath(); ctx.arc(nx, ny, r + 12, 0, Math.PI * 2); ctx.fillStyle = grad; ctx.fill(); // Circle ctx.beginPath(); ctx.arc(nx, ny, isH ? r + 3 : r, 0, Math.PI * 2); ctx.fillStyle = '#1e293b'; ctx.strokeStyle = nodeColor; ctx.lineWidth = 2.5; ctx.fill(); ctx.stroke(); // Inner dot if (!node.isolated) { ctx.beginPath(); ctx.arc(nx, ny, 3, 0, Math.PI * 2); ctx.fillStyle = nodeColor; ctx.fill(); } // Label if (node.type === 'brand' || isH) { ctx.fillStyle = '#cbd5e1'; ctx.font = '10px Inter, sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; ctx.fillText(node.name, nx, ny + r + 5); } }); if (Math.random() < 0.08) spawnPacket(); state.canvasAnimId = requestAnimationFrame(animateKG); } // ---- Copy / Export ---- function copyToClipboard(t) { if (!t) return Promise.reject('Empty'); if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(t); return new Promise((res, rej) => { const ta = document.createElement('textarea'); ta.value = t; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); res(); } catch (e) { rej(e); } finally { document.body.removeChild(ta); } }); } function copyReport() { const e = document.getElementById('reportOutput'); if (e) copyToClipboard(e.textContent).then(() => showToast('Rapport copié !')).catch(() => showToast('Échec', true)); } function copyStrategy() { const e = document.getElementById('strategyOutput'); if (e) copyToClipboard(e.textContent).then(() => showToast('Stratégie copiée !')).catch(() => showToast('Échec', true)); } function exportReport() { const e = document.getElementById('reportOutput'); if (!e || !e.textContent || e.textContent === 'En attente de génération...') { showToast('Lancez d\'abord la construction', true); return; } const b = new Blob([e.textContent], { type: 'text/plain' }); const u = URL.createObjectURL(b); const a = document.createElement('a'); a.href = u; a.download = `knowledge-graph-${state.domain.replace(/[^a-zA-Z0-9]/g, '_')}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(u); showToast('Rapport exporté !'); } function switchTab(tab, btn) { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); if (btn) btn.classList.add('active'); document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden')); const c = document.getElementById('tab-' + tab); if (c) c.classList.remove('hidden'); } function addLog(message, type = 'info') { const now = new Date(); const timeStr = now.toTimeString().split(' ')[0]; state.logs.unshift({ message, type, time: timeStr }); if (state.logs.length > 50) state.logs.pop(); renderLogs(); } function renderLogs() { const el = document.getElementById('activityLog'); if (!el) return; const cm = { info: 'text-slate-400', success: 'text-green-400', warning: 'text-amber-400', error: 'text-red-400', system: 'text-violet-400' }; const pm = { info: 'INF', success: 'OK ', warning: 'WRN', error: 'ERR', system: 'SYS' }; el.innerHTML = state.logs.map(l => `
${l.time}[${pm[l.type] || 'INF'}]${l.message}
`).join(''); } function clearLogs() { state.logs = []; renderLogs(); showToast('Journal effacé'); } function toggleMobileMenu() { const m = document.getElementById('mobileMenu'); if (m) m.classList.toggle('hidden'); } function showToast(message, isError) { const t = document.getElementById('toast'), m = document.getElementById('toastMsg'); if (!t || !m) return; m.textContent = message; const iconContainer = t.querySelector('.rounded-lg'); if (iconContainer) { const iconEl = iconContainer.querySelector('i'); if (iconEl) { if (isError) { iconContainer.className = 'w-8 h-8 bg-red-500/20 rounded-lg flex items-center justify-center'; iconEl.setAttribute('data-lucide', 'x'); iconEl.className = 'w-4 h-4 text-red-400'; } else { iconContainer.className = 'w-8 h-8 bg-violet-500/20 rounded-lg flex items-center justify-center'; iconEl.setAttribute('data-lucide', 'check'); iconEl.className = 'w-4 h-4 text-violet-400'; } lucide.createIcons(); } } t.classList.add('show'); setTimeout(() => t.classList.remove('show'), 2500); }