Download knowledge-graph.js from secutorpro/gdorks-web-generator-7cmse: direct link, hf CLI and curl.
- Browser
- Download file 62.3 kB
-
https://huggingface.co/spaces/secutorpro/gdorks-web-generator-7cmse/resolve/main/knowledge-graph.js
- Command line
-
hf download hf://spaces/secutorpro/gdorks-web-generator-7cmse/knowledge-graph.js
-
curl -L -o knowledge-graph.js https://huggingface.co/spaces/secutorpro/gdorks-web-generator-7cmse/resolve/main/knowledge-graph.js
62.3 kB
| // ===================================================== | |
| // 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 = '<div class="text-center text-slate-600 text-xs py-8">Aucune décision IA</div>'; 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 `<div class="ai-decision p-2.5 bg-slate-950/50 rounded-lg border border-slate-800/60" style="animation-delay:${i * 40}ms"><div class="flex items-start gap-2"><i data-lucide="cpu" class="w-3.5 h-3.5 text-violet-400 mt-0.5 shrink-0"></i><div class="flex-1 min-w-0"><div class="flex items-center gap-2 mb-0.5"><span class="text-xs font-medium text-slate-200">${d.decision}</span><span class="text-[9px] font-mono ${tc} ${bc} border ${bd} px-1 py-0.5 rounded uppercase font-bold">${d.priority}</span></div><div class="text-[10px] text-slate-500 font-mono">Impact: ${d.impact} · Confiance: ${(d.confidence * 100).toFixed(0)}%</div></div></div></div>`; | |
| }).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 => `<div class="flex items-center justify-between mb-0.5"><span class="text-slate-400">${p.type}</span><span class="text-violet-400">${(p.confidence * 100).toFixed(0)}%</span></div>`).join(''); else pe.textContent = '—'; } | |
| if (pre) { if (this.predictions.length > 0) pre.innerHTML = this.predictions.map(p => `<div class="mb-1"><div class="text-slate-400 text-[10px]">${p.prediction}</div><div class="text-amber-400 text-[9px] font-mono">Prob: ${(p.probability * 100).toFixed(0)}% · ${p.timeframe}</div></div>`).join(''); else pre.textContent = '—'; } | |
| }, | |
| renderLearning() { | |
| const el = document.getElementById('aiLearning'); if (!el) return; | |
| if (this.geneticGeneration > 0) el.innerHTML = `<div class="text-slate-400 text-[10px]">Génération: <span class="text-violet-400">${this.geneticGeneration}</span></div><div class="text-slate-400 text-[10px]">Fitness: <span class="text-green-400">${(this.bestFitness * 100).toFixed(1)}%</span></div>`; | |
| else if (this.iterations > 0) el.innerHTML = `<div class="text-slate-400 text-[10px]">Itérations: <span class="text-cyan-400">${this.iterations}</span></div><div class="text-slate-400 text-[10px]">Confiance: <span class="text-violet-400">${(this.confidence * 100).toFixed(0)}%</span></div>`; | |
| 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 = `<i data-lucide="loader" class="w-3 h-3 text-violet-400 animate-spin"></i><span>${stage.msg}</span>`; 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 => `<div><div class="flex items-center justify-between mb-1.5"><div class="flex items-center gap-2"><i data-lucide="${d.icon}" class="w-3.5 h-3.5" style="color:${d.color}"></i><span class="text-xs text-slate-300 font-medium">${d.name}</span></div><span class="text-xs font-mono" style="color:${d.color}">${d.value}/${d.max}</span></div><div class="score-bar-bg"><div class="score-bar-fill" style="width:${(d.value / d.max) * 100}%;background:linear-gradient(90deg,${d.color}80,${d.color});"></div></div><div class="text-[10px] text-slate-600 font-mono mt-0.5">${d.desc}</div></div>`).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 = '<div class="text-center text-slate-600 text-sm py-8">Aucune entité analysée</div>'; 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 `<div class="entity-item flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/50 border border-slate-800/60" style="animation-delay:${i * 20}ms"> | |
| <div class="w-8 h-8 bg-violet-500/10 border border-violet-500/20 rounded-md flex items-center justify-center shrink-0"><i data-lucide="${typeIcons[n.type] || 'circle'}" class="w-4 h-4 text-violet-400"></i></div> | |
| <div class="flex-1 min-w-0"> | |
| <div class="flex items-center gap-2 mb-0.5"><span class="text-xs font-medium text-slate-200 truncate">${n.name}</span><span class="text-[10px] font-mono text-violet-400 uppercase font-bold">${n.label}</span>${n.isolated ? '<span class="text-[10px] font-mono text-slate-500 uppercase">isolé</span>' : ''}</div> | |
| <div class="flex items-center gap-3 text-[10px] font-mono"> | |
| <span class="text-violet-400">T:${n.trust}</span> | |
| <span class="text-cyan-400">A:${n.authority}</span> | |
| <span class="${sentColor}"><i data-lucide="${sentIcon}" class="w-3 h-3 inline"></i> ${n.sentiment}</span> | |
| <span class="text-amber-400">I:${n.influence}</span> | |
| <span class="text-green-400">E:${n.eeat}</span> | |
| </div> | |
| </div> | |
| <span class="text-[10px] font-mono ${repColor} font-bold shrink-0">${repScore}</span> | |
| </div>`; | |
| }).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 `<div class="flex items-start gap-3 p-3 bg-slate-950/50 rounded-lg border border-slate-800/60" style="animation-delay:${i * 50}ms"><div class="w-8 h-8 ${bc} rounded-md flex items-center justify-center shrink-0"><i data-lucide="${r.icon}" class="w-4 h-4 ${tc}"></i></div><div class="flex-1 min-w-0"><div class="flex items-center gap-2 mb-1"><span class="text-xs font-medium text-slate-200">${r.title}</span><span class="text-[10px] font-mono ${ptc} ${pbc} border ${pbd} px-1.5 py-0.5 rounded uppercase font-semibold">${r.priority}</span></div><p class="text-[10px] text-slate-500 leading-relaxed">${r.desc}</p></div></div>`; | |
| }).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 = `<div class="text-white font-semibold">${state.hoveredNode.name}</div><div class="text-slate-400 text-[10px] uppercase">${state.hoveredNode.label}</div><div class="text-violet-400">T:${state.hoveredNode.trust} A:${state.hoveredNode.authority}</div><div class="text-purple-400">S:${state.hoveredNode.sentiment} I:${state.hoveredNode.influence}</div><div class="text-green-400">E:${state.hoveredNode.eeat}</div><div class="${repColor} font-bold mt-0.5">Réputation: ${repScore}/100</div>${state.hoveredNode.isolated ? '<div class="text-slate-500 font-bold">ISOLÉE</div>' : ''}`; | |
| } | |
| } 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 => `<div class="log-entry flex gap-2"><span class="text-slate-600 shrink-0">${l.time}</span><span class="${cm[l.type] || 'text-slate-400'} shrink-0">[${pm[l.type] || 'INF'}]</span><span class="text-slate-300">${l.message}</span></div>`).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); | |
| } |