@@ -526,6 +548,10 @@
+
+
+
+
diff --git a/public/css/tractatus-theme.css b/public/css/tractatus-theme.css
index e33a9917..18b66b7d 100644
--- a/public/css/tractatus-theme.css
+++ b/public/css/tractatus-theme.css
@@ -952,6 +952,33 @@ body.page-fade-out {
}
}
+/* ========================================
+ * DATA VISUALIZATIONS
+ * Pressure chart and timeline components
+ * ======================================== */
+.gauge-fill-path {
+ transition: stroke 0.3s ease;
+}
+
+.timeline-event {
+ transition: all 0.3s ease;
+}
+
+.timeline-event:hover {
+ transform: scale(1.05);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .gauge-fill-path,
+ .timeline-event {
+ transition: none !important;
+ }
+
+ .timeline-event:hover {
+ transform: none !important;
+ }
+}
+
/* ========================================
* DARK MODE SUPPORT (Future)
* Placeholder for dark mode implementation
diff --git a/public/css/tractatus-theme.min.css b/public/css/tractatus-theme.min.css
index dba75731..26978f9b 100644
--- a/public/css/tractatus-theme.min.css
+++ b/public/css/tractatus-theme.min.css
@@ -732,6 +732,28 @@ body.page-fade-out {
transition: none !important;
}
}
+/* ========================================
+ * DATA VISUALIZATIONS
+ * Pressure chart and timeline components
+ * ======================================== */
+.gauge-fill-path {
+ transition: stroke 0.3s ease;
+}
+.timeline-event {
+ transition: all 0.3s ease;
+}
+.timeline-event:hover {
+ transform: scale(1.05);
+}
+@media (prefers-reduced-motion: reduce) {
+ .gauge-fill-path,
+ .timeline-event {
+ transition: none !important;
+ }
+ .timeline-event:hover {
+ transform: none !important;
+ }
+}
/* ========================================
* DARK MODE SUPPORT (Future)
* Placeholder for dark mode implementation
diff --git a/public/js/components/activity-timeline.js b/public/js/components/activity-timeline.js
new file mode 100644
index 00000000..b918a6e4
--- /dev/null
+++ b/public/js/components/activity-timeline.js
@@ -0,0 +1,136 @@
+/**
+ * Framework Activity Timeline
+ * Tractatus Framework - Phase 3: Data Visualization
+ *
+ * Visual timeline showing framework component interactions
+ * Color-coded by service
+ */
+
+class ActivityTimeline {
+ constructor(containerId) {
+ this.container = document.getElementById(containerId);
+ if (!this.container) {
+ console.error(`[ActivityTimeline] Container #${containerId} not found`);
+ return;
+ }
+
+ this.events = [
+ {
+ time: '0ms',
+ service: 'instruction',
+ name: 'InstructionPersistence',
+ action: 'Load HIGH persistence instructions',
+ color: '#6366f1'
+ },
+ {
+ time: '50ms',
+ service: 'validator',
+ name: 'CrossReferenceValidator',
+ action: 'Verify request against instruction history',
+ color: '#8b5cf6'
+ },
+ {
+ time: '100ms',
+ service: 'boundary',
+ name: 'BoundaryEnforcer',
+ action: 'Check if request requires human approval',
+ color: '#10b981'
+ },
+ {
+ time: '150ms',
+ service: 'pressure',
+ name: 'ContextPressureMonitor',
+ action: 'Calculate current pressure level',
+ color: '#f59e0b'
+ },
+ {
+ time: '200ms',
+ service: 'metacognitive',
+ name: 'MetacognitiveVerifier',
+ action: 'Verify operation alignment',
+ color: '#ec4899'
+ },
+ {
+ time: '250ms',
+ service: 'deliberation',
+ name: 'PluralisticDeliberation',
+ action: 'Coordinate stakeholder perspectives',
+ color: '#14b8a6'
+ }
+ ];
+
+ this.init();
+ }
+
+ init() {
+ this.render();
+ console.log('[ActivityTimeline] Initialized');
+ }
+
+ render() {
+ const eventsHTML = this.events.map((event, index) => `
+
+
+ ${event.time}
+
+
+
+
+ ${event.name}
+
+
${event.action}
+
+
+ `).join('');
+
+ this.container.innerHTML = `
+
+
+
Governance Flow
+ Request Processing
+
+
+
+ ${eventsHTML}
+
+
+
+ Total processing time: 250ms | All services coordinated
+
+
+ `;
+
+ // Apply colors via JavaScript (CSP-compliant)
+ this.applyColors();
+ }
+
+ applyColors() {
+ document.querySelectorAll('.service-dot').forEach(dot => {
+ const color = dot.getAttribute('data-color');
+ dot.style.backgroundColor = color;
+ });
+
+ document.querySelectorAll('.service-name').forEach(name => {
+ const color = name.getAttribute('data-color');
+ name.style.color = color;
+ });
+ }
+}
+
+// Auto-initialize if container exists
+if (typeof window !== 'undefined') {
+ document.addEventListener('DOMContentLoaded', () => {
+ const container = document.getElementById('activity-timeline');
+ if (container) {
+ window.activityTimeline = new ActivityTimeline('activity-timeline');
+ }
+ });
+}
+
+// Export for module systems
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = ActivityTimeline;
+}
diff --git a/public/js/components/pressure-chart.js b/public/js/components/pressure-chart.js
new file mode 100644
index 00000000..d5bbcb10
--- /dev/null
+++ b/public/js/components/pressure-chart.js
@@ -0,0 +1,225 @@
+/**
+ * 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) {
+ this.container = document.getElementById(containerId);
+ 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() {
+ this.container.innerHTML = `
+
+
+
Context Pressure Monitor
+ NORMAL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ // 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')
+ };
+ }
+
+ attachEventListeners() {
+ this.elements.simulateBtn.addEventListener('click', () => this.simulate());
+ this.elements.resetBtn.addEventListener('click', () => this.reset());
+ }
+
+ 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
+ const centerX = 150;
+ const centerY = 120;
+ const radius = 120;
+ const startX = 30;
+ 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);
+ this.elements.status.textContent = status;
+ const statusClasses = 'text-xs font-medium uppercase';
+ const colorClass = level < 25 ? 'text-green-600' :
+ level < 50 ? 'text-amber-600' :
+ level < 75 ? 'text-red-600' : 'text-red-800';
+ this.elements.status.className = `${statusClasses} ${colorClass}`;
+
+ // 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() {
+ // Simulate pressure increasing from current to 85%
+ const targetLevels = [30, 50, 70, 85];
+ let index = 0;
+
+ const step = () => {
+ if (index >= targetLevels.length) return;
+ this.setLevel(targetLevels[index]);
+ index++;
+ setTimeout(step, 1500);
+ };
+
+ step();
+ }
+
+ reset() {
+ this.setLevel(0);
+ }
+}
+
+// Auto-initialize if container exists
+if (typeof window !== 'undefined') {
+ document.addEventListener('DOMContentLoaded', () => {
+ const container = document.getElementById('pressure-chart');
+ if (container) {
+ window.pressureChart = new PressureChart('pressure-chart');
+ }
+ });
+}
+
+// Export for module systems
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = PressureChart;
+}