/** * Interactive Architecture Diagram Component * Tractatus Framework - Phase 3: Interactive Architecture Diagram * * Handles click/hover interactions on the hexagonal service diagram * Shows service details in a side panel */ class InteractiveDiagram { constructor() { this.serviceData = { boundary: { name: 'BoundaryEnforcer', shortName: 'Boundary', color: '#10b981', icon: '🔒', description: 'Blocks AI from making values decisions (privacy, ethics, strategic direction). Requires human approval.', details: [ 'Enforces Tractatus 12.1-12.7 boundaries', 'Values decisions architecturally require humans', 'Prevents AI autonomous decision-making on ethical questions', 'External enforcement - harder to bypass via prompting' ], promise: 'Values boundaries enforced externally—harder to manipulate through prompting.' }, instruction: { name: 'InstructionPersistenceClassifier', shortName: 'Instruction', color: '#6366f1', icon: '📋', description: 'Stores instructions externally with persistence levels (HIGH/MEDIUM/LOW). Aims to reduce directive fade.', details: [ 'Quadrant-based classification (STR/OPS/TAC/SYS/STO)', 'Time-persistence metadata tagging', 'Temporal horizon modeling (STRATEGIC, OPERATIONAL, TACTICAL)', 'External storage independent of AI runtime' ], promise: 'Instructions stored outside AI—more resistant to context manipulation.' }, validator: { name: 'CrossReferenceValidator', shortName: 'Validator', color: '#8b5cf6', icon: '✓', description: 'Validates AI actions against instruction history. Aims to prevent pattern bias overriding explicit directives.', details: [ 'Cross-references AI claims with external instruction history', 'Detects pattern-based overrides of explicit user directives', 'Independent verification layer', 'Helps prevent instruction drift' ], promise: 'Independent verification—AI claims checked against external source.' }, pressure: { name: 'ContextPressureMonitor', shortName: 'Pressure', color: '#f59e0b', icon: '⚡', description: 'Monitors AI performance degradation. Escalates when context pressure threatens quality.', details: [ 'Tracks token usage, complexity, error rates', 'Detects degraded operating conditions', 'Adjusts verification requirements under pressure', 'Objective metrics for quality monitoring' ], promise: 'Objective metrics may detect manipulation attempts early.' }, metacognitive: { name: 'MetacognitiveVerifier', shortName: 'Metacognitive', color: '#ec4899', icon: '💡', description: 'Requires AI to pause and verify complex operations before execution. Structural safety check.', details: [ 'AI self-checks alignment, coherence, safety before execution', 'Structural pause-and-verify gates', 'Selective verification (not constant)', 'Architectural enforcement of reflection steps' ], promise: 'Architectural gates aim to enforce verification steps.' }, deliberation: { name: 'PluralisticDeliberationOrchestrator', shortName: 'Deliberation', color: '#14b8a6', icon: '👥', description: 'Facilitates multi-stakeholder deliberation for values conflicts where no single "correct" answer exists.', details: [ 'Non-hierarchical coordination for values conflicts', 'Stakeholder perspective representation', 'Consensus-building for ethical trade-offs', 'Addresses values pluralism in AI safety' ], promise: 'Facilitates deliberation across stakeholder perspectives for values conflicts.' } }; this.activeService = null; this.init(); } init() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.setup()); } else { this.setup(); } console.log('[InteractiveDiagram] Initialized'); } setup() { const svg = document.getElementById('interactive-arch-diagram'); if (!svg) { console.warn('[InteractiveDiagram] SVG diagram not found'); return; } const nodes = svg.querySelectorAll('.service-node'); console.log(`[InteractiveDiagram] Found ${nodes.length} service nodes`); nodes.forEach(node => { const serviceId = node.getAttribute('data-service'); node.addEventListener('click', (e) => { e.preventDefault(); this.showServiceDetails(serviceId); }); node.addEventListener('mouseenter', () => { this.highlightService(serviceId); }); node.addEventListener('mouseleave', () => { this.unhighlightService(serviceId); }); }); this.addKeyboardNavigation(nodes); } highlightService(serviceId) { const svg = document.getElementById('interactive-arch-diagram'); if (!svg) return; const connectionLine = svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.add('active'); } const node = svg.querySelector(`#node-${serviceId}`); if (node) { node.classList.add('hover'); } } unhighlightService(serviceId) { const svg = document.getElementById('interactive-arch-diagram'); if (!svg) return; if (this.activeService === serviceId) return; const connectionLine = svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.remove('active'); } const node = svg.querySelector(`#node-${serviceId}`); if (node) { node.classList.remove('hover'); } } showServiceDetails(serviceId) { const service = this.serviceData[serviceId]; if (!service) { console.error('[InteractiveDiagram] Service not found:', serviceId); return; } this.activeService = serviceId; const svg = document.getElementById('interactive-arch-diagram'); if (svg) { svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active')); svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active')); const node = svg.querySelector(`#node-${serviceId}`); if (node) { node.classList.add('active'); } const connectionLine = svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.add('active'); } } this.renderServicePanel(service); console.log('[InteractiveDiagram] Showing details for:', service.name); } renderServicePanel(service) { let panel = document.getElementById('service-detail-panel'); if (!panel) { panel = document.createElement('div'); panel.id = 'service-detail-panel'; panel.className = 'bg-white rounded-xl shadow-2xl p-6 border-2'; panel.style.borderColor = service.color; const container = document.querySelector('#diagram-container'); if (container) { container.appendChild(panel); } else { console.error('[InteractiveDiagram] Diagram container not found'); return; } } else { panel.style.borderColor = service.color; } const html = `
${service.icon}

${service.name}

${service.shortName}

${service.description}

Key Features

Early Promise: ${service.promise}
`; panel.innerHTML = html; // Apply styles via JavaScript (CSP-compliant) const iconBox = panel.querySelector('.service-icon-box'); if (iconBox) { const color = iconBox.getAttribute('data-color'); iconBox.style.background = `linear-gradient(135deg, ${color} 0%, ${color}dd 100%)`; } // Style all check icons const checkIcons = panel.querySelectorAll('.service-check-icon'); checkIcons.forEach(icon => { const color = icon.getAttribute('data-color'); icon.style.color = color; }); // Style promise badge const promiseBadge = panel.querySelector('.service-promise-badge'); if (promiseBadge) { const color = promiseBadge.getAttribute('data-color'); promiseBadge.style.backgroundColor = color; } // Style promise text const promiseText = panel.querySelector('.service-promise-text'); if (promiseText) { const color = promiseText.getAttribute('data-color'); promiseText.style.color = color; } // Add close button event listener (CSP-compliant) const closeBtn = panel.querySelector('#close-panel-btn'); if (closeBtn) { closeBtn.addEventListener('click', () => this.closePanel()); } panel.style.opacity = '0'; panel.style.transform = 'translateY(20px)'; panel.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; setTimeout(() => { panel.style.opacity = '1'; panel.style.transform = 'translateY(0)'; }, 10); } closePanel() { const panel = document.getElementById('service-detail-panel'); if (panel) { panel.style.opacity = '0'; panel.style.transform = 'translateY(20px)'; setTimeout(() => { panel.remove(); }, 300); } const svg = document.getElementById('interactive-arch-diagram'); if (svg) { svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active')); svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active')); } this.activeService = null; } addKeyboardNavigation(nodes) { nodes.forEach((node, index) => { node.setAttribute('tabindex', '0'); node.setAttribute('role', 'button'); node.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); const serviceId = node.getAttribute('data-service'); this.showServiceDetails(serviceId); } }); }); } } if (typeof window !== 'undefined') { window.interactiveDiagram = new InteractiveDiagram(); } if (typeof module !== 'undefined' && module.exports) { module.exports = InteractiveDiagram; }