⚡ CENTRO DE CONTROL DE OPERACIONES • PROTOCOL: BINARY RED/GREEN

Plataforma Unificada de Ingeniería, Workflows y Marketing

Gestiona el ciclo de vida completo de tus sistemas: desde la formulación de directivas con IA y diagramación vectorial, hasta la ejecución de grafos DAG en vivo, sincronización de tareas To-Do y despacho masivo de correos corporativos.

Esquema Relacional
15 Tablas
Neon PostgreSQL AWS Cloud
Latencia WebSockets
< 180 ms
Salas aisladas por Project ID
Validador Criptográfico
HMAC-SHA256
GitHub Webhooks Verificados
Despachador de Correos
BullMQ + Redis
RFC 8058 & Umbral ≥95%

❖ Catálogo Central de Módulos y Herramientas

Selecciona cualquier herramienta para acceder de forma instantánea a sus funciones operativas.

🧠
AI Generator

Antigravity Prompt Architect

Área de trabajo en texto plano para ideas de proyectos. Procesa tu texto descriptivo y pre-rellena automáticamente el checklist interactivo de desarrollo de 5 fases para generar directivas de sistema precisas.

Idea Parser 5-Phase Checklist System Directive Exporter Local Storage Sync
✓
Colaborativo

Gestor de Tareas To-Do Binario

Gestor de tareas manuales con prioridades (Low, Medium, High, Critical) vinculado a los nodos del grafo. Cuando el 100% de las tareas de un nodo pasan a DONE, el nodo transmuta de inmediato a GREEN.

Task CRUD Role: COLLABORATOR Real-time Pulse Neon DB Persisted
✉
BullMQ + Redis

Motor de Mercadotecnia y Envíos Masivos

Segmentación de audiencias con verificación contra listas de supresión, cabeceras RFC 8058 One-Click Unsubscribe, inyección de píxel 1x1, enlaces 302 y regla de completado ≥95% para validar el nodo de marketing a GREEN.

BullMQ Queue Suppression List Pixel Tracking 95% Success Trigger
📐
Diseño SVG CAD

Generador de Lockups Técnicos Vectoriales

Suite visual de diseño vectorial para generar lockups de arquitectura, sellos técnicos, diagramas de ingeniería y exportación limpia en formatos SVG de alta precisión y rasterizados PNG.

SVG Engine Technical Lockups Vector Exports Canvas Studio
📊
Finanzas & ROI

Modelos de Proyección Comercial y ROI

Documentación ejecutiva y modelo de retorno de inversión para el proyecto de modernización MPS Ayala. Incluye cálculo de optimización operativa, amortización fiscal y desglose de servicios.

ROI Projections Tax Strategy Executive Reports Client Facing
⚡
Backend API

Consola de Servicios REST y Webhooks

Especificación de endpoints backend, validador de firmas HMAC-SHA256 para GitHub Webhooks (X-Hub-Signature-256), y conexión en vivo a la base de datos distribuida Neon PostgreSQL.

Express + TS HMAC-SHA256 Auth Neon Connection Pool REST v1

⚡ Demostrador Interactivo: DAG & Transición Binaria de Estados

Prueba en tiempo real cómo los eventos de tareas y webhooks activan la regla binaria Rojo/Verde.

ESTADO: 1 DE 5 COMPLETADOS (20%)
INFRA GREEN
1. Infra & Neon DB
2/2 tareas completadas
→
BACKEND RED
2. Servicios Core API
1/2 tareas completadas
→
FRONTEND RED
3. UI & React Flow
0/2 tareas completadas
→
DEVOPS RED
4. Telemetría GitHub
0/1 webhook exitoso
→
MARKETING RED
5. Campaña Masiva
0% de entrega (Requiere ≥95%)

✓ Mutación de Tareas Neon DB

⚡ GitHub Webhook Telemetry

✉ BullMQ Email Dispatch

🤖 AI Task Planner

📊 AI Executive Status

⚡ AI DevOps Triage

✉️ AI Marketing Copy

🏗️ AI Blueprint DAG

📽️ AI Slide Engine

🧠 Nexus pgvector Brain

🔒 Cierre Inmutable

SOCKET.IO LOG STREAM [project:59b89995] Limpiar Consola
[18:45:00] WS client connected to room 'project:59b89995-9684-4de6-9291-f7a6806b999a'
[18:45:01] System initialized: 5 nodes in DAG. Initial binary rule applied.
function filterCards(category) { document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active')); event.target.classList.add('active'); const cards = document.querySelectorAll('.app-card'); cards.forEach(card => { if (category === 'all' || card.dataset.category === category) { card.style.display = 'flex'; } else { card.style.display = 'none'; } }); } function scrollToShowcase() { document.getElementById('interactive-demo').scrollIntoView({ behavior: 'smooth' }); } // LOGGING SYSTEM function addLog(msg, type = 'normal') { const consoleBox = document.getElementById('log-console'); const time = new Date().toTimeString().split(' ')[0]; const div = document.createElement('div'); div.className = `log-entry ${type === 'pulse' ? 'event-pulse' : (type === 'transition' ? 'event-transition' : '')}`; div.innerHTML = `[${time}] ${msg}`; consoleBox.appendChild(div); consoleBox.scrollTop = consoleBox.scrollHeight; } function clearLogs() { document.getElementById('log-console').innerHTML = ''; addLog('Consola reiniciada.', 'normal'); } // SIMULACIÓN DE NODOS Y REGLA BINARIA const nodeStates = { 'node-infra': true, 'node-backend': false, 'node-frontend': false, 'node-telemetry': false, 'node-marketing': false }; function updateOverallMetrics() { let completed = Object.values(nodeStates).filter(Boolean).length; let pct = (completed / 5) * 100; document.getElementById('overall-status-text').innerText = `${completed} DE 5 COMPLETADOS (${pct}%)`; } // CONFIGURACIÓN CENTRALIZADA Y ESTADO EN VIVO const ACTIVE_PROJECT_ID = '59b89995-9684-4de6-9291-f7a6806b999a'; let currentRole = 'COLLABORATOR'; let liveTasks = []; let liveNodes = []; let lastGeneratedSubtasks = []; function changeActiveRole(newRole) { currentRole = newRole; addLog(`[RBAC] Rol de usuario establecido a: ${currentRole}`, 'pulse'); if (currentRole === 'CLIENT') { addLog('[RBAC ALERTA] Modo CLIENT activado: Mutaciones y creación de tareas restringidas (403 Forbidden).', 'transition'); } } // SINCRONIZACIÓN EN VIVO CON NEON POSTGRESQL async function syncWithNeonDB() { const lbl = document.getElementById('lbl-db-status'); const dot = document.getElementById('dot-db-status'); if (lbl) lbl.innerText = 'SINCRONIZANDO...'; try { const [wfRes, tasksRes] = await Promise.all([ fetch(`/api/v1/projects/${ACTIVE_PROJECT_ID}/workflow`, { headers: { 'x-user-role': currentRole } }).then(r => r.json()), fetch(`/api/v1/projects/${ACTIVE_PROJECT_ID}/tasks`, { headers: { 'x-user-role': currentRole } }).then(r => r.json()) ]); if (wfRes && wfRes.nodes) { liveNodes = wfRes.nodes; } if (Array.isArray(tasksRes)) { liveTasks = tasksRes; } const categoryMap = { 'infra': 'node-infra', 'backend': 'node-backend', 'frontend': 'node-frontend', 'devops': 'node-telemetry', 'marketing': 'node-marketing' }; liveNodes.forEach(node => { const elId = categoryMap[node.category]; if (!elId) return; const el = document.getElementById(elId); const pill = document.getElementById(`pill-${elId}`); const countEl = document.getElementById(`count-${elId}`); nodeStates[elId] = (node.status === 'GREEN'); if (el) { if (node.status === 'GREEN') { el.classList.add('green'); } else { el.classList.remove('green'); } } if (pill) { pill.innerText = node.status; } if (countEl) { if (node.category === 'devops') { countEl.innerText = node.status === 'GREEN' ? '1/1 workflow_run exitoso' : '0/1 workflow_run (Pendiente)'; } else if (node.category === 'marketing') { countEl.innerText = node.status === 'GREEN' ? '98.5% tasa de entrega (≥95%)' : '0% tasa de entrega (Requiere ≥95%)'; } else { const nodeTasks = liveTasks.filter(t => t.node_id === node.id || (t.title && t.title.toLowerCase().includes(node.category))); const done = nodeTasks.filter(t => t.status === 'DONE').length; countEl.innerText = `${done}/${Math.max(nodeTasks.length, 2)} tareas completadas`; } } }); updateOverallMetrics(); if (lbl) { lbl.innerText = `ONLINE (${liveTasks.length} tareas)`; lbl.style.color = 'var(--accent-green)'; } if (dot) dot.classList.remove('red'); } catch (err) { if (lbl) { lbl.innerText = 'CONEXIÓN INTERMITENTE'; lbl.style.color = 'var(--accent-red)'; } if (dot) dot.classList.add('red'); addLog(`[NEON SYNC WARN] ${err.message}`, 'normal'); } } // MUTACIÓN REAL DE TAREAS EN NEON POSTGRESQL async function mutateTaskReal(taskId, newStatus, taskName) { if (currentRole === 'CLIENT') { addLog(`[HTTP 403 FORBIDDEN] Mutación rechazada para tarea '${taskName}'. El rol CLIENT es de sólo lectura.`, 'pulse'); alert('Acceso Denegado (HTTP 403 Forbidden): El rol CLIENT tiene permisos estrictamente de sólo lectura según las directivas del sistema.'); return; } addLog(`[HTTP PATCH /api/v1/tasks/${taskId}/status] status: ${newStatus} (Rol: ${currentRole})...`, 'normal'); try { const res = await fetch(`/api/v1/tasks/${taskId}/status`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'x-user-role': currentRole }, body: JSON.stringify({ status: newStatus, user_role: currentRole }) }); if (!res.ok) { const errData = await res.json().catch(() => ({})); throw new Error(errData.error || `HTTP ${res.status}`); } const data = await res.json(); addLog(`[NEON DB] Tarea '${taskName}' -> ${newStatus} guardada en PostgreSQL.`, 'pulse'); if (data.nodeTransitions && data.nodeTransitions.length > 0) { data.nodeTransitions.forEach(trans => { addLog(`[WS EMIT] WORKFLOW_NODE_TRANSITION: Transición binaria -> Nodo ${trans.status}`, 'transition'); }); } await syncWithNeonDB(); } catch (err) { addLog(`[MUTATION ERROR]: ${err.message}`, 'pulse'); alert(`Error al actualizar tarea: ${err.message}`); } } // ACCIONES ESPECÍFICAS PARA EL PANEL function toggleBackendTaskNeon() { const task = liveTasks.find(t => t.id === 'f17dcbf9-8ac8-44d6-bf51-8cab5999a840') || { status: 'TODO' }; const target = task.status === 'DONE' ? 'TODO' : 'DONE'; mutateTaskReal('f17dcbf9-8ac8-44d6-bf51-8cab5999a840', target, 'Rutas CRUD de tareas'); } function toggleInfraTaskNeon() { const task = liveTasks.find(t => t.id === '3af53ce9-b025-47d9-ad62-1b029cb80b2d') || { status: 'TODO' }; const target = task.status === 'DONE' ? 'TODO' : 'DONE'; mutateTaskReal('3af53ce9-b025-47d9-ad62-1b029cb80b2d', target, 'Ejecutar esquema DDL'); } function toggleNodeState(nodeId, nodeName) { if (nodeId === 'node-backend') { toggleBackendTaskNeon(); return; } if (nodeId === 'node-infra') { toggleInfraTaskNeon(); return; } nodeStates[nodeId] = !nodeStates[nodeId]; const el = document.getElementById(nodeId); const pill = document.getElementById(`pill-${nodeId}`); if (nodeStates[nodeId]) { el.classList.add('green'); pill.innerText = 'GREEN'; addLog(`[SIMULACIÓN] Nodo '${nodeName}' -> GREEN`, 'transition'); } else { el.classList.remove('green'); pill.innerText = 'RED'; addLog(`[SIMULACIÓN] Nodo '${nodeName}' -> RED`, 'transition'); } updateOverallMetrics(); } function simulateGitHubWebhook() { const el = document.getElementById('node-telemetry'); const pill = document.getElementById(`pill-node-telemetry`); const count = document.getElementById(`count-node-telemetry`); nodeStates['node-telemetry'] = true; el.classList.add('green'); pill.innerText = 'GREEN'; count.innerText = '1/1 workflow_run exitoso (sha: 032064f)'; addLog("[HTTP POST /api/v1/telemetry/github] HMAC-SHA256 firma criptográfica verificada OK (Status 200)", 'pulse'); addLog("[WS EMIT] TELEMETRY_PULSE: workflow_run 'CI Build' conclusion: success (34s)", 'pulse'); addLog("[WS EMIT] WORKFLOW_NODE_TRANSITION: Node 'Telemetría GitHub' -> GREEN", 'transition'); updateOverallMetrics(); } function simulateGitHubWebhookFail() { const el = document.getElementById('node-telemetry'); const pill = document.getElementById(`pill-node-telemetry`); const count = document.getElementById(`count-node-telemetry`); nodeStates['node-telemetry'] = false; el.classList.remove('green'); pill.innerText = 'RED'; count.innerText = '0/1 workflow_run (Falló o pendiente)'; addLog("[HTTP POST /api/v1/telemetry/github] workflow_run conclusion: failure", 'pulse'); addLog("[WS EMIT] WORKFLOW_NODE_TRANSITION: Node 'Telemetría GitHub' -> RED", 'transition'); updateOverallMetrics(); } function simulateMarketingCampaign() { const el = document.getElementById('node-marketing'); const pill = document.getElementById(`pill-node-marketing`); const count = document.getElementById(`count-node-marketing`); nodeStates['node-marketing'] = true; el.classList.add('green'); pill.innerText = 'GREEN'; count.innerText = '98.5% tasa de entrega exitosa (≥95%)'; addLog("[BULLMQ QUEUE] Job #8491 completado: 250 enviados, 0 rebotes, 2 bajas", 'pulse'); addLog("[WS EMIT] CAMPAIGN_TELEMETRY_PULSE: Campaña alcanzó 98.5% de entrega", 'pulse'); addLog("[WS EMIT] WORKFLOW_NODE_TRANSITION: Node 'Campaña Masiva' -> GREEN (≥95% umbral superado)", 'transition'); updateOverallMetrics(); } function resetMarketingCampaign() { const el = document.getElementById('node-marketing'); const pill = document.getElementById(`pill-node-marketing`); const count = document.getElementById(`count-node-marketing`); nodeStates['node-marketing'] = false; el.classList.remove('green'); pill.innerText = 'RED'; count.innerText = '0% de entrega (Requiere ≥95%)'; addLog("[BULLMQ QUEUE] Estado reiniciado a DRAFT", 'normal'); addLog("[WS EMIT] WORKFLOW_NODE_TRANSITION: Node 'Campaña Masiva' -> RED", 'transition'); updateOverallMetrics(); } // MODAL: 1. AI TASK PLANNER (DESGLOSAR TAREAS CON IA) function triggerAIDecomposeTask() { document.getElementById('modal-title').innerText = '🤖 AI Task Planner: Desglosar Requerimientos'; document.getElementById('modal-content').innerHTML = `
Fallback Engine: Activo
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAIDecomposeTask() { const title = document.getElementById('ai-task-title-input').value.trim(); const desc = document.getElementById('ai-task-desc-input').value.trim(); const btn = document.getElementById('btn-run-ai-task'); const resultsBox = document.getElementById('ai-task-results'); if (!title) { alert('Por favor ingresa un título para la tarea.'); return; } btn.disabled = true; btn.innerHTML = ` Consultando AI Gateway...`; addLog(`[AI GATEWAY] Desglosando tarea: "${title}"...`, 'pulse'); try { const res = await fetch('/api/v1/ai/tasks/breakdown', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: ACTIVE_PROJECT_ID, taskTitle: title, taskDescription: desc }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); const plan = data.plan; lastGeneratedSubtasks = plan.subtasks || []; let subtasksHtml = lastGeneratedSubtasks.map((st, i) => `
${i+1}. ${st.title}
Criterio: ${st.acceptance_criteria}
Rol: ${st.suggested_role || 'COLLABORATOR'}
`).join(''); resultsBox.innerHTML = `
Complejidad Estimada: ${plan.estimated_complexity || 'MEDIUM'} (${lastGeneratedSubtasks.length} subtareas)
${subtasksHtml} `; addLog(`[AI GATEWAY] ${lastGeneratedSubtasks.length} subtareas generadas con éxito y validadas con Zod.`, 'transition'); } catch (err) { addLog(`[AI ERROR] Falló el desglose: ${err.message}`, 'pulse'); resultsBox.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `✨ Desglosar con Gemini AI`; } } async function saveSubtasksToNeon() { if (currentRole === 'CLIENT') { alert('Acceso Denegado (HTTP 403 Forbidden): El rol CLIENT no puede crear tareas.'); return; } if (!lastGeneratedSubtasks || lastGeneratedSubtasks.length === 0) { alert('No hay subtareas generadas para guardar.'); return; } addLog(`[NEON DB] Insertando ${lastGeneratedSubtasks.length} subtareas en PostgreSQL...`, 'pulse'); try { let inserted = 0; for (const st of lastGeneratedSubtasks) { await fetch(`/api/v1/projects/${ACTIVE_PROJECT_ID}/tasks`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-user-role': currentRole }, body: JSON.stringify({ title: st.title, description: st.acceptance_criteria, priority: 'MEDIUM', user_role: currentRole }) }); inserted++; } addLog(`[NEON DB] ${inserted} subtareas persistidas correctamente en project_tasks.`, 'transition'); alert(`¡Éxito! Se insertaron ${inserted} subtareas en la base de datos Neon.`); await syncWithNeonDB(); } catch (err) { alert(`Error al guardar en Neon: ${err.message}`); } } // MODAL: 2. AI EXECUTIVE REPORT (CONSULTA VIVA DE POSTGRESQL + GEMINI) function triggerAIExecutiveReport() { document.getElementById('modal-title').innerText = '📊 Reporte Ejecutivo en Vivo para Clientes'; document.getElementById('modal-content').innerHTML = `
Este reporte consulta el estado real de los nodos en Neon PostgreSQL, métricas de avance y CI/CD, sintetizándolos mediante el AI Gateway en un formato comprensible para directores de negocio.
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAIExecutiveReport() { const btn = document.getElementById('btn-run-exec-report'); const box = document.getElementById('ai-exec-results'); btn.disabled = true; btn.innerHTML = ` Consultando Neon DB & AI Gateway...`; addLog(`[AI GATEWAY] Consultando estado consolidado en PostgreSQL para reporte ejecutivo...`, 'pulse'); try { const res = await fetch(`/api/v1/ai/executive-report/${ACTIVE_PROJECT_ID}`); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); const rep = data.report; const healthColor = rep.overall_health === 'ON_TRACK' ? 'var(--accent-green)' : (rep.overall_health === 'AT_RISK' ? 'var(--accent-amber)' : 'var(--accent-red)'); box.innerHTML = `
Salud Global del Proyecto: ${rep.overall_health}
${rep.executive_summary}
Hitos Concluidos:
Focos de Próxima Entrega:
Impacto de Negocio: ${rep.business_impact}
`; addLog(`[AI GATEWAY] Reporte ejecutivo generado y validado con Zod (HTTP 200).`, 'transition'); } catch (err) { addLog(`[AI ERROR] Reporte falló: ${err.message}`, 'pulse'); box.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `📊 Regenerar Reporte`; } } // MODAL: 3. AI DEVOPS TRIAGE (ANÁLISIS DE FALLOS CI/CD) function triggerAIDevOpsTriage() { document.getElementById('modal-title').innerText = '⚡ AI DevOps Triage: Diagnóstico SRE de CI/CD'; document.getElementById('modal-content').innerHTML = `
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAIDevOpsTriage() { const logText = document.getElementById('ai-ci-log-input').value.trim(); const btn = document.getElementById('btn-run-triage'); const box = document.getElementById('ai-triage-results'); if (!logText) { alert('Por favor introduce un log de compilación para analizar.'); return; } btn.disabled = true; btn.innerHTML = ` Analizando logs con SRE AI...`; addLog(`[AI GATEWAY] Analizando log de CI/CD (${logText.length} bytes)...`, 'pulse'); try { const res = await fetch('/api/v1/ai/triage/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: ACTIVE_PROJECT_ID, failureLog: logText, workflowPath: '.github/workflows/ci.yml', commitMessage: 'fix(workflow): update node coordinate types' }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); const tri = data.triage; box.innerHTML = `
Causa Raíz Identificada (Severidad: ${tri.severity})
${tri.root_cause}
Comando Técnico Correctivo Sugerido
${tri.suggested_fix}
Explicación Digerible para Clientes
${tri.client_friendly_summary}
`; addLog(`[AI GATEWAY] Diagnóstico SRE generado con recomendación accionable.`, 'transition'); } catch (err) { addLog(`[AI ERROR] Triage falló: ${err.message}`, 'pulse'); box.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `🔍 Diagnosticar con IA`; } } // MODAL: 4. AI MARKETING COPY & ANTISPAM function triggerAIMarketingCopy() { document.getElementById('modal-title').innerText = '✉️ AI Email Copy & Evaluación Antispam'; document.getElementById('modal-content').innerHTML = `
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAIMarketingCopy() { const goal = document.getElementById('ai-mkt-goal').value.trim(); const audience = document.getElementById('ai-mkt-audience').value.trim(); const tone = document.getElementById('ai-mkt-tone').value.trim(); const btn = document.getElementById('btn-run-mkt'); const box = document.getElementById('ai-mkt-results'); btn.disabled = true; btn.innerHTML = ` Generando copy y analizando entregabilidad...`; addLog(`[AI GATEWAY] Generando copy de marketing para objetivo: "${goal}"...`, 'pulse'); try { const res = await fetch('/api/v1/ai/marketing/generate-copy', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: ACTIVE_PROJECT_ID, campaignGoal: goal, targetAudience: audience, tone: tone }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); const copy = data.copy; box.innerHTML = `
Puntaje de Riesgo Antispam: ${copy.spam_risk_score} / 10 (Excelente)
LÍNEAS DE ASUNTO SUGERIDAS:
TEXTO PRE-HEADER:
${copy.preview_text}
PREVISUALIZACIÓN DE CUERPO (TEXTO PLANO):
${copy.plain_text_body}
`; addLog(`[AI GATEWAY] Plantilla de correo y análisis antispam generado con éxito.`, 'transition'); } catch (err) { addLog(`[AI ERROR] Falló copy de marketing: ${err.message}`, 'pulse'); box.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `✉️ Redactar Campaña & Auditar Antispam`; } } // MODAL: 5. AI BLUEPRINT ENGINE (GRAFO DAG) function triggerAIBlueprint() { document.getElementById('modal-title').innerText = '🏗️ AI Blueprint Engine: Arquitectura de Workflows'; document.getElementById('modal-content').innerHTML = `
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAIBlueprint() { const desc = document.getElementById('ai-bp-desc').value.trim(); const stack = document.getElementById('ai-bp-stack').value.trim(); const btn = document.getElementById('btn-run-bp'); const box = document.getElementById('ai-bp-results'); btn.disabled = true; btn.innerHTML = ` Diseñando DAG con Principal Architect AI...`; addLog(`[AI GATEWAY] Diseñando Grafo Acíclico Dirigido (DAG)...`, 'pulse'); try { const res = await fetch('/api/v1/ai/blueprint/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: ACTIVE_PROJECT_ID, projectDescription: desc, techStack: { backend: 'Node.js', database: 'Neon Postgres', ci: 'GitHub Actions', frontend: 'React' } }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); const dag = data.dag; box.innerHTML = `
✓ Grafo DAG Generado (${dag.nodes.length} Nodos, ${dag.edges.length} Aristas)
${dag.nodes.map(n => `
${n.title} [${n.category}] ${n.tasks ? n.tasks.length : 0} tareas
`).join('')}
`; addLog(`[AI GATEWAY] Grafo DAG generado exitosamente y formateado para @xyflow/react.`, 'transition'); } catch (err) { addLog(`[AI ERROR] Falló Blueprint: ${err.message}`, 'pulse'); box.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `📐 Generar Grafo DAG & Nodos con IA`; } } // ========================================================================= // 6. AI SLIDE ENGINE: DIAPOSITIVAS EJECUTIVAS TSOLUTIONS IPIDD (TERA / GAMMA) // ========================================================================= let currentPresentationSlides = []; let currentPresentationIndex = 0; let currentPresentationTitle = ''; async function triggerAISlideEngine() { document.getElementById('modal-title').innerText = '📽️ AI Slide Engine: Diapositivas Ejecutivas TSolutions IPIDD'; document.getElementById('modal-content').innerHTML = `
Lema Rector: "Tecnología instalada. Conocimiento transferido. Negocios escalados."
Generador de diapositivas ejecutivas 16:9 bloqueado bajo los tokens corporativos de TSolutions IPIDD.
`; document.getElementById('guide-modal').classList.add('open'); } async function executeAISlideEngine() { const title = document.getElementById('ai-slide-title').value.trim(); const objective = document.getElementById('ai-slide-objective').value.trim(); const context = document.getElementById('ai-slide-context').value.trim(); const btn = document.getElementById('btn-run-slides'); const box = document.getElementById('ai-slide-results'); if (!title || !objective) { alert('Por favor completa el título y el objetivo.'); return; } btn.disabled = true; btn.innerHTML = ` Orquestando con AI Gateway & Persistiendo en Neon...`; addLog(`[AI GATEWAY] Generando diapositivas ejecutivas para: "${title}"...`, 'pulse'); try { const res = await fetch('/api/v1/presentations/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: ACTIVE_PROJECT_ID, title, objective, contextSummary: context }) }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || `HTTP ${res.status}`); } const data = await res.json(); currentPresentationSlides = data.slides || []; currentPresentationTitle = data.deck.title; currentPresentationIndex = 0; box.innerHTML = `
${data.deck.title}
✓ ${currentPresentationSlides.length} diapositivas modulares generadas y persistidas en Neon PostgreSQL
${renderSlideHTML(currentPresentationSlides[0], 1, currentPresentationSlides.length)}
${currentPresentationSlides.map((s, idx) => `
#${idx + 1} ${s.layout_type} ${(s.slide_data.title || 'Slide').slice(0, 15)}
`).join('')}
`; addLog(`[AI GATEWAY] Mazo de diapositivas ejecutivas guardado en PostgreSQL (${currentPresentationSlides.length} slides).`, 'transition'); } catch (err) { addLog(`[AI ERROR] Falló Slide Engine: ${err.message}`, 'pulse'); box.innerHTML = `
Error: ${err.message}
`; } finally { btn.disabled = false; btn.innerHTML = `✨ Generar Mazo 16:9 con IA`; } } function switchPreviewSlide(idx) { currentPresentationIndex = idx; const container = document.getElementById('live-slide-preview-container'); if (container && currentPresentationSlides[idx]) { container.innerHTML = renderSlideHTML(currentPresentationSlides[idx], idx + 1, currentPresentationSlides.length); } currentPresentationSlides.forEach((_, sIdx) => { const thumb = document.getElementById(`thumb-slide-${sIdx}`); if (thumb) { thumb.style.borderColor = sIdx === idx ? '#FF6B00' : 'rgba(255,255,255,0.15)'; } }); } async function loadSavedPresentationsList() { const box = document.getElementById('ai-slide-results'); box.innerHTML = `
Consultando mazos en Neon PostgreSQL...
`; try { const res = await fetch('/api/v1/presentations'); const decks = await res.json(); if (decks.length === 0) { box.innerHTML = `
No hay mazos registrados todavía.
`; return; } box.innerHTML = `
SELECCIONA UN MAZO PARA VISUALIZAR:
${decks.map(d => `
${d.title}
${d.slides_count || 0} diapositivas • Creado: ${new Date(d.created_at).toLocaleDateString()}
Ver Mazo ➔
`).join('')}
`; } catch (err) { box.innerHTML = `
Error al listar mazos: ${err.message}
`; } } async function selectDeckForPreview(deckId) { const box = document.getElementById('ai-slide-results'); box.innerHTML = `
Cargando diapositivas...
`; try { const res = await fetch(`/api/v1/presentations/${deckId}`); const data = await res.json(); currentPresentationSlides = data.slides || []; currentPresentationTitle = data.deck.title; currentPresentationIndex = 0; box.innerHTML = `
${data.deck.title}
${renderSlideHTML(currentPresentationSlides[0], 1, currentPresentationSlides.length)}
${currentPresentationSlides.map((s, idx) => `
#${idx + 1} ${s.layout_type} ${(s.slide_data.title || 'Slide').slice(0, 15)}
`).join('')}
`; } catch (err) { box.innerHTML = `
Error al cargar diapositivas: ${err.message}
`; } } function renderSlideHTML(slide, current, total) { if (!slide) return '
Sin diapositiva disponible
'; const d = typeof slide.slide_data === 'string' ? JSON.parse(slide.slide_data) : slide.slide_data; const type = slide.layout_type; let bodyHTML = ''; if (type === 'HERO_COVER') { bodyHTML = `
${d.badgeText || 'AUDITORÍA OPEX 2026'}

${d.title}

${d.subtitle || ''}

CONSULTORÍA: ${d.author || 'TSolutions IPIDD'}
•
CLIENTE: ${d.client || 'Comité Directivo'}
•
FECHA: ${d.date || '2026-09-30'}
`; } else if (type === 'THREE_CARD_KPI') { const cards = d.cards || []; bodyHTML = `

${d.title}

${d.subtitle || ''}

${cards.map((c, i) => { const color = i === 0 ? '#FF6B00' : (i === 1 ? '#00E5FF' : '#F59E0B'); return `
${c.badge || 'KPI'}
${c.value}
${c.label}
${c.description}
`; }).join('')}
`; } else if (type === 'TWO_COLUMN') { bodyHTML = `

${d.title}

${d.subtitle || ''}

${d.leftBadge || 'AS-IS'}

${d.leftTitle}

    ${(d.leftPoints || []).map(p => `
  • ${p}
  • `).join('')}
${d.rightBadge || 'TO-BE'}

${d.rightTitle}

    ${(d.rightPoints || []).map(p => `
  • ${p}
  • `).join('')}
`; } else if (type === 'TIMELINE_ROADMAP') { const phases = d.phases || []; bodyHTML = `

${d.title}

${d.subtitle || ''}

${phases.map((p, i) => { const color = p.status === 'COMPLETED' ? '#00E5FF' : (p.status === 'IN_PROGRESS' ? '#FF6B00' : '#94A3B8'); return `
${p.step}
${p.duration}
${p.phaseName}
${(p.deliverables || []).join(' • ')}
`; }).join('')}
`; } else { // Formato Genérico / Cierre / Ishikawa bodyHTML = `
${type}

${d.title}

${d.summary || d.problemSummary || ''}

${d.ctaButtonText ? ` ` : ''}
`; } return `
TSOLUTIONS IPIDD ${type}
${String(current).padStart(2, '0')} / ${String(total).padStart(2, '0')}
${bodyHTML}
TSOLUTIONS IPIDD • SYSTEM ARCHITECTURE & OPEX
Tecnología instalada. Conocimiento transferido. Negocios escalados.
`; } // REPRODUCTOR PANTALLA COMPLETA function launchFullscreenPresenter() { if (currentPresentationSlides.length === 0) return; let overlay = document.getElementById('ts-fullscreen-presenter-overlay'); if (!overlay) { overlay = document.createElement('div'); overlay.id = 'ts-fullscreen-presenter-overlay'; overlay.style.position = 'fixed'; overlay.style.inset = '0'; overlay.style.background = '#05070B'; overlay.style.zIndex = '99999'; overlay.style.display = 'flex'; overlay.style.flexDirection = 'column'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; overlay.style.padding = '20px'; document.body.appendChild(overlay); } overlay.innerHTML = `
${currentPresentationTitle}
${renderSlideHTML(currentPresentationSlides[currentPresentationIndex], currentPresentationIndex + 1, currentPresentationSlides.length)}
Usa Espacio o Flechas para navegar
`; overlay.style.display = 'flex'; // Atajos de teclado en pantalla completa window.onkeydown = function(e) { if (overlay.style.display === 'flex') { if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') { e.preventDefault(); fullscreenNextSlide(); } else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); fullscreenPrevSlide(); } else if (e.key === 'Escape') { closeFullscreenPresenter(); } } }; } function fullscreenNextSlide() { if (currentPresentationIndex < currentPresentationSlides.length - 1) { currentPresentationIndex++; const target = document.getElementById('fullscreen-slide-body'); if (target) { target.innerHTML = renderSlideHTML(currentPresentationSlides[currentPresentationIndex], currentPresentationIndex + 1, currentPresentationSlides.length); } } } function fullscreenPrevSlide() { if (currentPresentationIndex > 0) { currentPresentationIndex--; const target = document.getElementById('fullscreen-slide-body'); if (target) { target.innerHTML = renderSlideHTML(currentPresentationSlides[currentPresentationIndex], currentPresentationIndex + 1, currentPresentationSlides.length); } } } function closeFullscreenPresenter() { const overlay = document.getElementById('ts-fullscreen-presenter-overlay'); if (overlay) overlay.style.display = 'none'; window.onkeydown = null; } // MODAL DE INFORMACIÓN RÁPIDA / GUÍA function openQuickGuideModal() { document.getElementById('modal-title').innerText = 'Guía Integral del Ecosistema Nexus'; document.getElementById('modal-content').innerHTML = `

Bienvenido a Nexus Architect Suite. Esta plataforma centraliza todas las herramientas desarrolladas para la ingeniería, diseño y comercialización del proyecto:

Tip: En el simulador inferior puedes accionar los botones de simulación para observar las transiciones de eventos y logs de WebSocket en tiempo real.

`; document.getElementById('guide-modal').classList.add('open'); } function openManualModal() { document.getElementById('modal-title').innerText = 'Manual Integral & Sugerencias de Optimización'; document.getElementById('modal-content').innerHTML = `

📘 Documento de Arquitectura y Manual Operativo

El manual exhaustivo con todas las funciones, endpoints y esquemas está persistido en el archivo raíz MANUAL_DE_USUARIO_Y_ARQUITECTURA.md.

1. Resumen de Módulos y Funcionalidades

2. Sugerencias Clave de Optimización

• Caché de Grafos en Redis: Almacenar en memoria la estructura del DAG para reducir consultas repetitivas a Neon PostgreSQL, invalidando la clave solo ante eventos WORKFLOW_NODE_TRANSITION.
• Despacho por Lotes (Batching) en BullMQ: Empaquetar envíos en bloques de 50 correos por job para optimizar el rendimiento de la cola y conexiones concurrentes.
• Validación Estricta con Zod: Proteger todos los controladores REST y Webhooks con schemas Zod para tipado seguro en tiempo de ejecución.
• Virtualización de Tareas (React Virtual): Implementar @tanstack/react-virtual en la lista To-Do para soportar proyectos con cientos de tareas sin degradación del DOM.
• Autenticación Robusta con Neon Auth: Integrar sesiones JWT y rotación de credenciales para reemplazar el paso de headers de rol en peticiones API.
`; document.getElementById('guide-modal').classList.add('open'); } function closeModal() { document.getElementById('guide-modal').classList.remove('open'); } function closeModalOnBackdrop(e) { if (e.target.id === 'guide-modal') { closeModal(); } } // ========================================================================= // JAVASCRIPT: CEREBRO NEXUS, BIBLIOTECA ARCHIVADOS & CIERRE FORMAL // ========================================================================= let currentLoadedSnapshot = null; // 1. NEXUS CHAT function openNexusChatModal() { document.getElementById('nexus-chat-modal').style.display = 'flex'; setTimeout(() => { document.getElementById('nexus-query-input')?.focus(); }, 100); } function closeNexusChatModal() { document.getElementById('nexus-chat-modal').style.display = 'none'; } async function sendNexusQuery(customQuery) { const inputEl = document.getElementById('nexus-query-input'); const domainEl = document.getElementById('nexus-domain-select'); const queryText = (customQuery || inputEl.value || '').trim(); if (!queryText) return; const domain = domainEl ? domainEl.value : ''; const messagesContainer = document.getElementById('nexus-chat-messages'); // 1. Render User Message const userBubble = document.createElement('div'); userBubble.style.alignSelf = 'flex-end'; userBubble.style.maxWidth = '80%'; userBubble.innerHTML = `
TÚ (OPERACIONES) ${new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'})}
${escapeHtml(queryText)}
`; messagesContainer.appendChild(userBubble); inputEl.value = ''; messagesContainer.scrollTop = messagesContainer.scrollHeight; // 2. Render Loading Indicator const loadingBubble = document.createElement('div'); loadingBubble.id = 'nexus-loading-indicator'; loadingBubble.style.alignSelf = 'flex-start'; loadingBubble.innerHTML = `
Buscando en pgvector (HNSW) y sintetizando respuesta de Nexus...
`; messagesContainer.appendChild(loadingBubble); messagesContainer.scrollTop = messagesContainer.scrollHeight; try { const resp = await fetch('/api/v1/nexus/query', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: queryText, domain_filter: domain || undefined, min_similarity: 0.70 }) }); const data = await resp.json(); document.getElementById('nexus-loading-indicator')?.remove(); // 3. Render Nexus Answer const nexusBubble = document.createElement('div'); nexusBubble.style.alignSelf = 'flex-start'; nexusBubble.style.maxWidth = '90%'; let citedHtml = ''; if (data.citedProjects && data.citedProjects.length > 0) { citedHtml = `
Proyectos Citados: ${data.citedProjects.map(p => `[Proyecto: ${escapeHtml(p)}]`).join('')}
`; } let sourcesHtml = ''; if (data.sources && data.sources.length > 0) { sourcesHtml = `
Ver ${data.sources.length} fragmentos recuperados de la memoria vectorial
${data.sources.map(s => `
${escapeHtml(s.title)} Afinidad: ${(s.similarity * 100).toFixed(1)}%
${escapeHtml(s.rawContent)}
`).join('')}
`; } nexusBubble.innerHTML = `
NEXUS BRAIN ${new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'})} ⚡ ${data.queryLatencyMs}ms
${formatMarkdown(data.answer || 'Sin respuesta generada')}
${citedHtml} ${sourcesHtml}
`; messagesContainer.appendChild(nexusBubble); messagesContainer.scrollTop = messagesContainer.scrollHeight; } catch (err) { document.getElementById('nexus-loading-indicator')?.remove(); const errBubble = document.createElement('div'); errBubble.style.alignSelf = 'flex-start'; errBubble.innerHTML = `
⚠️ Error al consultar a Nexus: ${escapeHtml(err.message)}
`; messagesContainer.appendChild(errBubble); } } // 2. BIBLIOTECA DE ARCHIVADOS function openArchivedLibraryModal() { document.getElementById('archived-library-modal').style.display = 'flex'; loadArchivedProjects(); } function closeArchivedLibraryModal() { document.getElementById('archived-library-modal').style.display = 'none'; } async function loadArchivedProjects() { const container = document.getElementById('archived-projects-container'); container.innerHTML = '
Cargando proyectos archivados...
'; const searchVal = document.getElementById('archived-search-input')?.value || ''; const stackVal = document.getElementById('archived-stack-filter')?.value || ''; const ratingVal = document.getElementById('archived-rating-filter')?.value || '0'; try { const params = new URLSearchParams(); if (searchVal) params.append('search', searchVal); if (stackVal) params.append('stack', stackVal); if (parseInt(ratingVal) > 0) params.append('minRating', ratingVal); const resp = await fetch(`/api/v1/projects/archived?${params.toString()}`); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); const projects = await resp.json(); if (projects.length === 0) { container.innerHTML = `
📂

No hay proyectos archivados con estos filtros

Cierra formalmente un proyecto activo para archivar su snapshot inmutable y alimentar a Nexus.

`; return; } container.innerHTML = projects.map(p => `
ARCHIVADO INMUTABLE ${'★'.repeat(p.client_feedback_rating || 5)}

${escapeHtml(p.name)}

${escapeHtml(p.description || 'Proyecto concluido y auditado.')}

$${(Number(p.final_roi_amount)||0).toLocaleString()}
ROI Final
${p.final_cycle_time_days || 0}d
Ciclo
${p.final_sigma_level || 5.9}σ
Six Sigma
Firmante: ${escapeHtml(p.client_sign_off_name || 'Stakeholder')} • Vectores: ${p.vectors_count || 5}
`).join(''); } catch (err) { container.innerHTML = `
Error al cargar biblioteca: ${escapeHtml(err.message)}
`; } } // 3. DETALLE DEL SNAPSHOT INMUTABLE async function viewArchivedSnapshot(projectId) { document.getElementById('snapshot-modal').style.display = 'flex'; document.getElementById('snapshot-project-title').innerText = 'Cargando snapshot inmutable...'; document.getElementById('snapshot-tab-body').innerHTML = '
Recuperando snapshot JSONB desde Neon PostgreSQL...
'; try { const resp = await fetch(`/api/v1/projects/${projectId}/snapshot`); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); currentLoadedSnapshot = await resp.json(); document.getElementById('snapshot-project-title').innerText = currentLoadedSnapshot.project_name || 'Proyecto Archivado'; switchSnapshotTab('kpis'); } catch (err) { document.getElementById('snapshot-tab-body').innerHTML = `
Error al obtener snapshot: ${escapeHtml(err.message)}
`; } } function closeSnapshotModal() { document.getElementById('snapshot-modal').style.display = 'none'; currentLoadedSnapshot = null; } function switchSnapshotTab(tabName) { if (!currentLoadedSnapshot) return; document.querySelectorAll('.snap-tab-btn').forEach(b => { b.style.background = 'transparent'; b.style.color = '#94A3B8'; b.style.fontWeight = 'normal'; }); const activeBtn = document.getElementById(`snap-tab-${tabName}`); if (activeBtn) { activeBtn.style.background = '#FF6B00'; activeBtn.style.color = '#FFF'; activeBtn.style.fontWeight = 'bold'; } const body = document.getElementById('snapshot-tab-body'); const snap = currentLoadedSnapshot; const snapData = snap.snapshot_data || {}; if (tabName === 'kpis') { body.innerHTML = `
$${Number(snap.final_roi_amount || 0).toLocaleString()}
RETORNO DE INVERSIÓN FINAL
${snap.final_cycle_time_days || 0} DÍAS
TIEMPO DE CICLO CONCLUIDO
${snap.final_sigma_level || 5.9} σ
CALIDAD SIX SIGMA

Conformidad y Sign-off del Cliente

Representante Firmante: ${escapeHtml(snap.client_sign_off_name || '')}

Calificación: ${'★'.repeat(snap.client_feedback_rating || 5)} (${snap.client_feedback_rating}/5)

"${escapeHtml(snap.client_comments || snapData.closure?.client_comments || 'Sin comentarios adicionales.')}"

`; } else if (tabName === 'dag') { const nodes = snapData.workflow_dag?.nodes || []; body.innerHTML = `

Topología DAG 100% en GREEN al Cierre

${nodes.map(n => `
${escapeHtml(n.category || 'core')} GREEN ✓
${escapeHtml(n.title || n.label)}
Completado: Auditoría Cierre Inmutable
`).join('')}
`; } else if (tabName === 'opex') { const fiveWhys = snapData.opex?.five_whys || []; const ishikawaFactors = snapData.opex?.ishikawa_factors || []; body.innerHTML = `

Causas Raíz y 5 Porqués Consolidados

${fiveWhys.map(fw => `
Problema: ${escapeHtml(fw.problem)}
Causa Raíz Confirmada: ${escapeHtml(fw.root_cause)}
`).join('')} ${fiveWhys.length === 0 ? '

No se registraron 5 Porqués en este proyecto.

' : ''}
`; } else if (tabName === 'cert') { const certHtml = snapData.executive_certificate?.certificate_html || snap.closure_certificate_pdf || '

Acta formal archivada

'; body.innerHTML = certHtml; } } // 4. CIERRE FORMAL DEL PROYECTO function openProjectClosureModal() { document.getElementById('project-closure-modal').style.display = 'flex'; } function closeProjectClosureModal() { document.getElementById('project-closure-modal').style.display = 'none'; } async function submitProjectClosure(e) { e.preventDefault(); const signOff = document.getElementById('closure-sign-off-name').value.trim(); const rating = parseInt(document.getElementById('closure-rating').value, 10); const roi = parseFloat(document.getElementById('closure-roi').value) || 0; const sigma = parseFloat(document.getElementById('closure-sigma').value) || 5.9; const comments = document.getElementById('closure-comments').value.trim(); const btn = document.getElementById('btn-submit-closure'); btn.disabled = true; btn.innerText = 'Sellando inmutablemente...'; try { const targetProjId = '59b89995-9684-4de6-9291-f7a6806b999a'; const resp = await fetch(`/api/v1/projects/${targetProjId}/close`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_sign_off_name: signOff, client_feedback_rating: rating, client_comments: comments, final_roi_amount: roi, final_sigma_level: sigma }) }); const data = await resp.json(); if (!resp.ok) throw new Error(data.error || 'Fallo al cerrar proyecto'); alert(`✓ Proyecto cerrado y archivado exitosamente.\nSnapshot inmutable generado y trabajo de asimilación encolado en Nexus.`); closeProjectClosureModal(); syncWithNeonDB(); } catch (err) { alert(`Error al archivar proyecto: ${err.message}`); } finally { btn.disabled = false; btn.innerText = 'Sellar y Archivar Proyecto 🔒'; } } // Helpers function escapeHtml(text) { if (!text) return ''; return String(text) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function formatMarkdown(text) { if (!text) return ''; return escapeHtml(text) .replace(/### (.*?)\n/g, '

$1

') .replace(/## (.*?)\n/g, '

$1

') .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/\[Proyecto: (.*?)\]/g, '[Proyecto: $1]') .replace(/\n/g, '
'); } // LISTENER WEBSOCKET PARA ASIMILACIÓN NEXUS if (typeof io !== 'undefined') { try { const socket = io(); socket.on('NEXUS_KNOWLEDGE_ABSORBED', (evt) => { console.log('[WebSocket Event]: NEXUS_KNOWLEDGE_ABSORBED', evt); addLog(`🧠 NEXUS ASIMILÓ: ${evt.absorbedCount} vectores del proyecto '${evt.projectName}'.`); }); socket.on('PROJECT_ARCHIVED', (evt) => { console.log('[WebSocket Event]: PROJECT_ARCHIVED', evt); addLog(`🔒 PROYECTO ARCHIVADO: '${evt.projectName}' ha pasado a modo inmutable solo lectura.`); }); } catch (err) { // Socket.io listener fallback } } // INICIALIZACIÓN AUTOMÁTICA window.addEventListener('DOMContentLoaded', () => { syncWithNeonDB(); // Polling ligero de sincronización cada 12 segundos setInterval(syncWithNeonDB, 12000); }); // ATAJOS DE TECLADO document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); if (e.key === '1') scrollToShowcase(); if (e.key === '2') window.location.href = 'prompt_architect.html'; if (e.key === '3') window.location.href = 'generador_lockups_tecnicos.html'; });