FLUJOS/POCS/VISUALIZACION/public/demos/demo_text_nodes.html
CAPITANSITO 25953376cd Reorganize POCs and demos under POCS/ folder
- Move BACK_BACK/ → POCS/BACK_BACK/ (image pipeline scripts)
- Move VISUALIZACION/ → POCS/VISUALIZACION/ (demos + static assets)
- No path changes needed: ../VISUALIZACION/public still resolves correctly
  from POCS/BACK_BACK/FLUJOS_APP_PRUEBAS.js
- Add FLUJOS_DATOS/DOCS/extraer_info_bbdd.txt (DB state snapshot + commands)

FLUJOS/ and FLUJOS_DATOS/ untouched (production).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 01:22:27 +02:00

242 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>FLUJOS — Demo: Text Nodes</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap" rel="stylesheet">
<script type="importmap">
{
"imports": {
"three": "https://esm.sh/three@0.168",
"three/": "https://esm.sh/three@0.168/",
"three-spritetext": "https://esm.sh/three-spritetext@1.9?external=three",
"3d-force-graph": "https://esm.sh/3d-force-graph@1.73?external=three"
}
}
</script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Fira Code', monospace; }
body { background: #000; color: #39ff14; overflow: hidden; }
#graph { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
#header {
position: fixed; top: 0; left: 0; right: 0; z-index: 10;
padding: 10px 20px;
background: linear-gradient(180deg, rgba(0,0,0,0.9) 0%, transparent 100%);
display: flex; align-items: center; gap: 16px;
}
#header h1 { font-size: 1.1em; color: #39ff14; letter-spacing: 3px; text-shadow: 0 0 10px #39ff14; }
#header .tag {
font-size: 0.65em; padding: 3px 8px;
border: 1px solid #39ff14; color: #39ff14;
opacity: 0.7; letter-spacing: 2px;
}
#info-panel {
position: fixed; bottom: 30px; right: 20px; z-index: 10;
width: 280px;
background: rgba(0,0,0,0.85);
border: 1px solid #39ff14;
padding: 16px;
display: none;
}
#info-panel h3 { font-size: 0.9em; color: #39ff14; margin-bottom: 8px; }
#info-panel .group-badge {
display: inline-block; padding: 2px 8px;
font-size: 0.7em; letter-spacing: 2px;
margin-bottom: 10px;
}
#info-panel p { font-size: 0.75em; color: #aaa; line-height: 1.5; }
#info-panel .close {
position: absolute; top: 8px; right: 10px;
cursor: pointer; color: #39ff14; font-size: 0.8em;
background: none; border: none; font-family: inherit;
}
#legend {
position: fixed; bottom: 30px; left: 20px; z-index: 10;
background: rgba(0,0,0,0.8); border: 1px solid #333;
padding: 12px 16px;
}
#legend h4 { font-size: 0.65em; color: #555; margin-bottom: 8px; letter-spacing: 2px; }
.legend-item { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; }
.legend-label { font-size: 0.65em; color: #888; }
#technique-label {
position: fixed; top: 50px; right: 20px; z-index: 10;
font-size: 0.6em; color: #333; letter-spacing: 1px;
text-align: right; line-height: 1.8;
}
</style>
</head>
<body>
<div id="graph"></div>
<div id="header">
<h1>FLUJOS</h1>
<span class="tag">TEXT NODES</span>
<span class="tag">three-spritetext</span>
</div>
<div id="info-panel">
<button class="close" onclick="document.getElementById('info-panel').style.display='none'"></button>
<h3 id="node-title"></h3>
<span class="group-badge" id="node-group"></span>
<p id="node-content"></p>
</div>
<div id="legend">
<h4>GRUPOS</h4>
<div class="legend-item"><div class="legend-dot" style="background:#39ff14"></div><span class="legend-label">core</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#ff4500"></div><span class="legend-label">climate</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#ff69b4"></div><span class="legend-label">security</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#00fff2"></div><span class="legend-label">journalism</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#ffdc00"></div><span class="legend-label">corporate</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#4488ff"></div><span class="legend-label">politics</span></div>
<div class="legend-item"><div class="legend-dot" style="background:#cc44ff"></div><span class="legend-label">data</span></div>
</div>
<div id="technique-label">
nodeThreeObject(node =&gt; SpriteText)<br>
nodeThreeObjectExtend(true)<br>
— three-spritetext —
</div>
<script type="module">
import ForceGraph3D from '3d-force-graph';
import SpriteText from 'three-spritetext';
const GROUP_COLORS = {
core: '#39ff14',
climate: '#ff4500',
security: '#ff69b4',
journalism: '#00fff2',
corporate: '#ffdc00',
politics: '#4488ff',
data: '#cc44ff'
};
const MOCK_DATA = {
nodes: [
{ id: 'FLUJOS', group: 'core', content: 'Sistema de visualización de flujos de información global' },
{ id: 'Cambio Climático', group: 'climate', content: 'Crisis climática y sus efectos sociopolíticos a escala global' },
{ id: 'Emisiones CO₂', group: 'climate', content: 'Emisiones de dióxido de carbono por sector industrial' },
{ id: 'Energía Renovable', group: 'climate', content: 'Transición energética hacia fuentes sostenibles' },
{ id: 'Pérdida Biodiversidad',group: 'climate', content: 'Extinción masiva de especies y colapso de ecosistemas' },
{ id: 'Seguridad Intl.', group: 'security', content: 'Geopolítica, conflictos armados y alianzas militares' },
{ id: 'Ciberseguridad', group: 'security', content: 'Ataques cibernéticos estatales y crimen organizado digital' },
{ id: 'Vigilancia Masiva', group: 'security', content: 'Programas de espionaje gubernamental (NSA, GCHQ...)' },
{ id: 'Privacidad Datos', group: 'security', content: 'Derechos digitales y protección de datos personales' },
{ id: 'Libertad de Prensa', group: 'journalism', content: 'Estado global de la libertad periodística' },
{ id: 'Desinformación', group: 'journalism', content: 'Fake news, propaganda y manipulación informativa' },
{ id: 'Whistleblowers', group: 'journalism', content: 'Filtraciones: Snowden, Assange, Panama Papers...' },
{ id: 'Periodismo de Datos', group: 'journalism', content: 'Investigación basada en datos abiertos y scraping' },
{ id: 'Eco-Corporativo', group: 'corporate', content: 'Poder corporativo y su influencia en política' },
{ id: 'Big Tech', group: 'corporate', content: 'Monopolios: Google, Meta, Amazon, Apple, Microsoft' },
{ id: 'Paraísos Fiscales', group: 'corporate', content: 'Evasión fiscal corporativa offshore' },
{ id: 'Lobbying', group: 'corporate', content: 'Grupos de presión e influencia legislativa' },
{ id: 'Populismo', group: 'politics', content: 'Auge de movimientos populistas globales' },
{ id: 'Elecciones', group: 'politics', content: 'Procesos electorales e interferencia exterior' },
{ id: 'Migración', group: 'politics', content: 'Crisis migratoria y políticas de fronteras' },
{ id: 'Extremismo', group: 'politics', content: 'Radicalización online y movimientos extremistas' },
{ id: 'Wikipedia', group: 'data', content: 'Enciclopedia libre como fuente de datos estructurados' },
{ id: 'Redes Sociales', group: 'data', content: 'Plataformas sociales como vectores de información' },
{ id: 'IA & Algoritmos', group: 'data', content: 'Inteligencia artificial, sesgos y control algorítmico' },
{ id: 'Torrents & P2P', group: 'data', content: 'Redes de distribución descentralizada de información' },
],
links: [
{ source: 'FLUJOS', target: 'Cambio Climático', value: 90 },
{ source: 'FLUJOS', target: 'Seguridad Intl.', value: 85 },
{ source: 'FLUJOS', target: 'Libertad de Prensa', value: 88 },
{ source: 'FLUJOS', target: 'Eco-Corporativo', value: 87 },
{ source: 'FLUJOS', target: 'Populismo', value: 82 },
{ source: 'FLUJOS', target: 'Wikipedia', value: 95 },
{ source: 'FLUJOS', target: 'Torrents & P2P', value: 88 },
{ source: 'Cambio Climático', target: 'Emisiones CO₂', value: 92 },
{ source: 'Cambio Climático', target: 'Energía Renovable', value: 88 },
{ source: 'Cambio Climático', target: 'Pérdida Biodiversidad',value: 85 },
{ source: 'Emisiones CO₂', target: 'Eco-Corporativo', value: 78 },
{ source: 'Energía Renovable', target: 'Big Tech', value: 65 },
{ source: 'Seguridad Intl.', target: 'Ciberseguridad', value: 88 },
{ source: 'Seguridad Intl.', target: 'Vigilancia Masiva', value: 85 },
{ source: 'Seguridad Intl.', target: 'Migración', value: 75 },
{ source: 'Ciberseguridad', target: 'Privacidad Datos', value: 90 },
{ source: 'Vigilancia Masiva', target: 'Privacidad Datos', value: 92 },
{ source: 'Vigilancia Masiva', target: 'Big Tech', value: 75 },
{ source: 'Vigilancia Masiva', target: 'IA & Algoritmos', value: 82 },
{ source: 'Libertad de Prensa',target: 'Desinformación', value: 88 },
{ source: 'Libertad de Prensa',target: 'Whistleblowers', value: 85 },
{ source: 'Libertad de Prensa',target: 'Periodismo de Datos', value: 82 },
{ source: 'Desinformación', target: 'Redes Sociales', value: 90 },
{ source: 'Desinformación', target: 'Elecciones', value: 85 },
{ source: 'Desinformación', target: 'Extremismo', value: 80 },
{ source: 'Whistleblowers', target: 'Ciberseguridad', value: 72 },
{ source: 'Periodismo de Datos',target:'Wikipedia', value: 80 },
{ source: 'Eco-Corporativo', target: 'Big Tech', value: 85 },
{ source: 'Eco-Corporativo', target: 'Paraísos Fiscales', value: 88 },
{ source: 'Eco-Corporativo', target: 'Lobbying', value: 90 },
{ source: 'Big Tech', target: 'IA & Algoritmos', value: 88 },
{ source: 'Big Tech', target: 'Privacidad Datos', value: 85 },
{ source: 'Big Tech', target: 'Redes Sociales', value: 92 },
{ source: 'Lobbying', target: 'Elecciones', value: 78 },
{ source: 'Populismo', target: 'Elecciones', value: 88 },
{ source: 'Populismo', target: 'Migración', value: 85 },
{ source: 'Populismo', target: 'Extremismo', value: 82 },
{ source: 'Extremismo', target: 'Redes Sociales', value: 78 },
{ source: 'IA & Algoritmos', target: 'Redes Sociales', value: 85 },
]
};
const elem = document.getElementById('graph');
const Graph = ForceGraph3D()(elem)
.backgroundColor('#000000')
.nodeLabel(node => `<span style="font-family:Fira Code,monospace;color:${GROUP_COLORS[node.group]}">${node.id}</span>`)
.nodeColor(node => GROUP_COLORS[node.group] || '#ffffff')
.nodeVal(node => node.group === 'core' ? 4 : 1.5)
.linkColor(link => {
const val = link.value || 50;
const alpha = Math.round((val / 100) * 200).toString(16).padStart(2, '0');
return `#39ff14${alpha}`;
})
.linkOpacity(0.4)
.linkWidth(link => (link.value || 50) / 60)
.nodeThreeObject(node => {
const sprite = new SpriteText(node.id);
sprite.material.depthWrite = false;
sprite.color = GROUP_COLORS[node.group] || '#ffffff';
sprite.textHeight = node.group === 'core' ? 5 : 3.5;
sprite.backgroundColor = 'rgba(0,0,0,0.4)';
sprite.padding = 2;
return sprite;
})
.nodeThreeObjectExtend(true)
.onNodeClick(node => {
const panel = document.getElementById('info-panel');
document.getElementById('node-title').textContent = node.id;
const badge = document.getElementById('node-group');
badge.textContent = node.group.toUpperCase();
badge.style.background = GROUP_COLORS[node.group] + '22';
badge.style.border = `1px solid ${GROUP_COLORS[node.group]}`;
badge.style.color = GROUP_COLORS[node.group];
document.getElementById('node-content').textContent = node.content || '';
panel.style.display = 'block';
})
.onNodeHover(node => { elem.style.cursor = node ? 'pointer' : null; })
.graphData(MOCK_DATA);
Graph.d3Force('charge').strength(-180);
setTimeout(() => Graph.zoomToFit(600, 80), 1200);
window.addEventListener('resize', () => {
Graph.width(elem.clientWidth).height(elem.clientHeight);
});
</script>
</body>
</html>