/** * Context Pressure Visualization * Tractatus Framework - Phase 3: Data Visualization * * Visual representation of Context Pressure Monitor metrics * Uses amber color scheme matching the ContextPressureMonitor service */ class PressureChart { constructor(containerId, gaugeContainerId = 'pressure-gauge') { this.container = document.getElementById(containerId); this.gaugeContainer = document.getElementById(gaugeContainerId); if (!this.container) { console.error(`[PressureChart] Container #${containerId} not found`); return; } this.currentLevel = 0; // 0-100 this.targetLevel = 0; this.animating = false; this.colors = { low: '#10b981', // Green - NORMAL moderate: '#f59e0b', // Amber - ELEVATED high: '#ef4444', // Red - HIGH critical: '#991b1b' // Dark Red - CRITICAL }; this.init(); } init() { this.render(); this.attachEventListeners(); console.log('[PressureChart] Initialized'); } render() { console.log('[PressureChart] render() called, container:', this.container); this.container.innerHTML = `

Context Pressure Monitor

NORMAL

Interactive Demo: Click "Simulate Pressure" to watch how context pressure builds. As token usage increases, tasks become more complex, and error rates rise. The framework monitors this relationship to detect when AI performance may degrade.

The timeline on the right shows how six governance components coordinate to validate each request and maintain safe operation.

0% Pressure Level
0
Tokens Used
Low
Complexity
0
Error Rate
`; // Clear gauge container if it exists (no longer needed) if (this.gaugeContainer) { this.gaugeContainer.innerHTML = ''; } // Store references this.elements = { gaugeFill: document.getElementById('gauge-fill'), gaugeValue: document.getElementById('gauge-value'), status: document.getElementById('pressure-status'), tokens: document.getElementById('metric-tokens'), complexity: document.getElementById('metric-complexity'), errors: document.getElementById('metric-errors'), simulateBtn: document.getElementById('pressure-simulate-btn'), resetBtn: document.getElementById('pressure-reset-btn') }; // Verify innerHTML was set console.log('[PressureChart] innerHTML length:', this.container.innerHTML.length); console.log('[PressureChart] First 100 chars:', this.container.innerHTML.substring(0, 100)); // Verify elements were found console.log('[PressureChart] Elements found:', { gaugeFill: !!this.elements.gaugeFill, gaugeValue: !!this.elements.gaugeValue, status: !!this.elements.status, simulateBtn: !!this.elements.simulateBtn, resetBtn: !!this.elements.resetBtn }); } attachEventListeners() { if (!this.elements.simulateBtn || !this.elements.resetBtn) { console.error('[PressureChart] Cannot attach event listeners - buttons not found'); return; } console.log('[PressureChart] Attaching event listeners to buttons'); this.elements.simulateBtn.addEventListener('click', () => this.simulate()); this.elements.resetBtn.addEventListener('click', () => this.reset()); console.log('[PressureChart] Event listeners attached successfully'); } setLevel(level) { this.targetLevel = Math.max(0, Math.min(100, level)); this.animateToTarget(); } animateToTarget() { if (this.animating) return; this.animating = true; const animate = () => { const diff = this.targetLevel - this.currentLevel; if (Math.abs(diff) < 0.5) { this.currentLevel = this.targetLevel; this.animating = false; this.updateGauge(); return; } this.currentLevel += diff * 0.1; this.updateGauge(); requestAnimationFrame(animate); }; animate(); } updateGauge() { const level = this.currentLevel; const angle = (level / 100) * 180; // 0-180 degrees const radians = (angle * Math.PI) / 180; // Calculate arc endpoint (20% smaller gauge: radius 96 instead of 120) const centerX = 150; const centerY = 120; const radius = 96; const startX = 54; const startY = 120; const endX = centerX + radius * Math.cos(Math.PI - radians); const endY = centerY - radius * Math.sin(Math.PI - radians); const largeArcFlag = angle > 180 ? 1 : 0; const path = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`; this.elements.gaugeFill.setAttribute('d', path); this.elements.gaugeValue.textContent = `${Math.round(level)}%`; // Update color based on level let color, status; if (level < 25) { color = this.colors.low; status = 'NORMAL'; } else if (level < 50) { color = this.colors.moderate; status = 'ELEVATED'; } else if (level < 75) { color = this.colors.high; status = 'HIGH'; } else { color = this.colors.critical; status = 'CRITICAL'; } this.elements.gaugeFill.setAttribute('stroke', color); // Update status badge with animation const previousStatus = this.elements.status.textContent; this.elements.status.textContent = status; // Badge styling based on level const baseClasses = 'px-4 py-2 rounded-full text-sm font-bold uppercase transition-all duration-500'; let bgClass, textClass; if (level < 25) { bgClass = 'bg-green-100'; textClass = 'text-green-700'; } else if (level < 50) { bgClass = 'bg-amber-100'; textClass = 'text-amber-700'; } else if (level < 75) { bgClass = 'bg-red-100'; textClass = 'text-red-700'; } else { bgClass = 'bg-red-200'; textClass = 'text-red-900'; } // Add pulse animation when status changes const pulseClass = previousStatus !== status ? 'animate-pulse' : ''; this.elements.status.className = `${baseClasses} ${bgClass} ${textClass} ${pulseClass}`; // Remove pulse after animation if (pulseClass) { setTimeout(() => { this.elements.status.className = `${baseClasses} ${bgClass} ${textClass}`; }, 1000); } // Update metrics based on pressure level const tokens = Math.round(level * 2000); // 0-200k tokens const complexityLevels = ['Low', 'Moderate', 'High', 'Extreme']; const complexityIndex = Math.min(3, Math.floor(level / 25)); const errorRate = Math.round(level / 5); // 0-20% this.elements.tokens.textContent = tokens.toLocaleString(); this.elements.complexity.textContent = complexityLevels[complexityIndex]; this.elements.errors.textContent = `${errorRate}%`; } simulate() { console.log('[PressureChart] Simulate button clicked - starting pressure simulation'); // Trigger timeline simulation if available if (window.activityTimeline) { console.log('[PressureChart] Triggering governance flow timeline'); window.activityTimeline.simulateFlow(); } // Simulate pressure increasing from current to 85% const targetLevels = [30, 50, 70, 85]; let index = 0; const step = () => { if (index >= targetLevels.length) return; console.log('[PressureChart] Setting pressure level to', targetLevels[index]); this.setLevel(targetLevels[index]); index++; setTimeout(step, 1500); }; step(); } reset() { console.log('[PressureChart] Reset button clicked'); // Reset timeline if available if (window.activityTimeline) { console.log('[PressureChart] Resetting governance flow timeline'); window.activityTimeline.reset(); } this.setLevel(0); } } // Auto-initialize if container exists if (typeof window !== 'undefined') { function initPressureChart() { console.log('[PressureChart] Attempting to initialize, readyState:', document.readyState); const container = document.getElementById('pressure-chart'); if (container) { console.log('[PressureChart] Container found, creating instance'); window.pressureChart = new PressureChart('pressure-chart'); } else { console.error('[PressureChart] Container #pressure-chart not found in DOM'); } } // Initialize immediately if DOM is already loaded, otherwise wait for DOMContentLoaded console.log('[PressureChart] Script loaded, readyState:', document.readyState); if (document.readyState === 'loading') { console.log('[PressureChart] Waiting for DOMContentLoaded'); document.addEventListener('DOMContentLoaded', initPressureChart); } else { console.log('[PressureChart] DOM already loaded, initializing immediately'); initPressureChart(); } } // Export for module systems if (typeof module !== 'undefined' && module.exports) { module.exports = PressureChart; }