/** * 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.activeService = null; this.loadTranslations(); this.init(); } loadTranslations() { // Get translations from i18n system const i18n = window.i18nTranslations || {}; const diagram = i18n.diagram_services || {}; // Define static properties (icons and colors) const staticProps = { overview: { color: '#0ea5e9', icon: '⚙️' }, boundary: { color: '#10b981', icon: '🔒' }, instruction: { color: '#6366f1', icon: '📋' }, validator: { color: '#8b5cf6', icon: '✓' }, pressure: { color: '#f59e0b', icon: '⚡' }, metacognitive: { color: '#ec4899', icon: '💡' }, deliberation: { color: '#14b8a6', icon: '👥' } }; // Build serviceData from translations this.serviceData = {}; Object.keys(staticProps).forEach(serviceId => { const trans = diagram[serviceId] || {}; const details = []; // Collect detail1-detail6 (some services have 4, overview has 6) for (let i = 1; i <= 6; i++) { if (trans[`detail${i}`]) { details.push(trans[`detail${i}`]); } } this.serviceData[serviceId] = { name: trans.name || serviceId, shortName: trans.shortName || serviceId, color: staticProps[serviceId].color, icon: staticProps[serviceId].icon, description: trans.description || '', details: details, promise: trans.promise || '' }; }); console.log('[InteractiveDiagram] Loaded translations for', Object.keys(this.serviceData).length, 'services'); } // FIXED: Better load detection with SVG verification const checkAndInit = () => { const svgDoc = objectElement.contentDocument; // Check if contentDocument exists and is actually SVG if (svgDoc && svgDoc.documentElement) { const rootTagName = svgDoc.documentElement.tagName ? svgDoc.documentElement.tagName.toLowerCase() : ''; if (rootTagName === 'svg') { // It's an SVG - safe to initialize console.log('[InteractiveDiagram] SVG detected in contentDocument, initializing'); initializeSVG(); return true; } else { console.log('[InteractiveDiagram] contentDocument exists but root is:', rootTagName, '- not ready yet'); return false; } } return false; }; // Try immediate initialization if already loaded if (!checkAndInit()) { // Not ready yet - wait for load event console.log('[InteractiveDiagram] Waiting for object to load...'); objectElement.addEventListener('load', () => { console.log('[InteractiveDiagram] Object load event fired'); // Small delay to ensure contentDocument is fully parsed setTimeout(() => { if (!checkAndInit()) { // Still not ready - start retry mechanism initializeSVG(); } }, 50); }); // Also try periodic checks as fallback let retryCount = 0; const maxRetries = 20; const retryInterval = setInterval(() => { retryCount++; if (checkAndInit() || retryCount >= maxRetries) { clearInterval(retryInterval); if (retryCount >= maxRetries) { console.error('[InteractiveDiagram] Failed to load SVG after', maxRetries, 'retries'); } } }, 100); } } highlightService(serviceId) { if (!this.svg) return; const connectionLine = this.svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.add('active'); } const node = this.svg.querySelector(`#node-${serviceId}`); if (node) { node.classList.add('hover'); } } unhighlightService(serviceId) { if (!this.svg) return; if (this.activeService === serviceId) return; const connectionLine = this.svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.remove('active'); } const node = this.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; if (this.svg) { this.svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active')); this.svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active')); const node = this.svg.querySelector(`#node-${serviceId}`); if (node) { node.classList.add('active'); } const connectionLine = this.svg.querySelector(`#conn-${serviceId}`); if (connectionLine) { connectionLine.classList.add('active'); } } this.renderServicePanel(service); console.log('[InteractiveDiagram] Showing details for:', service.name); } renderServicePanel(service) { const panel = document.getElementById('service-detail-panel'); if (!panel) { console.error('[InteractiveDiagram] Service detail panel not found in DOM'); return; } // Update border color to match selected service panel.style.borderColor = service.color; panel.style.borderWidth = '2px'; const html = `
${service.description}