diff --git a/app.py b/app.py
index b70b62e..8c18134 100644
--- a/app.py
+++ b/app.py
@@ -208,6 +208,28 @@ def process_kernel_map():
except Exception as e:
return jsonify({'error': str(e)}), 500
+@app.route('/api/processes')
+def get_processes():
+ """API for getting all Linux processes"""
+ try:
+ processes = []
+ for proc in psutil.process_iter(['pid', 'name', 'status', 'memory_info']):
+ try:
+ memory_info = proc.info['memory_info']
+ memory_mb = memory_info.rss / 1024 / 1024 # Convert to MB
+ processes.append({
+ 'pid': proc.info['pid'],
+ 'name': proc.info['name'],
+ 'status': proc.info['status'],
+ 'memory_mb': round(memory_mb, 1)
+ })
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
+ continue
+
+ return jsonify({'processes': processes})
+ except Exception as e:
+ return jsonify({'error': str(e)}), 500
+
@app.route('/health')
def health_check():
"""Application health check"""
diff --git a/index.html b/index.html
index ba0479d..a2f3503 100755
--- a/index.html
+++ b/index.html
@@ -6,6 +6,8 @@
Kernel AI
+
+
diff --git a/static/js/main.js b/static/js/main.js
index b492354..b45d08d 100755
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -268,6 +268,9 @@ function draw() {
// Load processes and kernel subsystems
loadProcessKernelMap(centerX, centerY);
+ // Draw additional process lines
+ drawProcessKernelMap2(centerX, centerY);
+
// Draw curves at bottom
drawLowerBezierGrid();
@@ -428,6 +431,68 @@ function drawProcessKernelMap(data, centerX, centerY) {
});
}
+// Draw additional process lines (without circles and names)
+function drawProcessKernelMap2(centerX, centerY) {
+ // Fetch all Linux processes
+ fetch('/api/processes')
+ .then(res => res.json())
+ .then(data => {
+ const processes = data.processes || [];
+ const numProcesses = processes.length;
+
+ // Find min and max memory usage for scaling
+ const memoryValues = processes.map(p => p.memory_mb || 0);
+ const minMemory = Math.min(...memoryValues);
+ const maxMemory = Math.max(...memoryValues);
+ const memoryRange = maxMemory - minMemory;
+
+ processes.forEach((process, i) => {
+ const angle = i * 2 * Math.PI / numProcesses;
+
+ // Calculate line length based on memory usage
+ const memoryMb = process.memory_mb || 0;
+ const memoryRatio = memoryRange > 0 ? (memoryMb - minMemory) / memoryRange : 0;
+
+ // Base distance: 250px (original), max additional: 100px based on memory
+ const baseDistance = 250;
+ const maxAdditionalDistance = 100;
+ const distance = baseDistance + (memoryRatio * maxAdditionalDistance);
+
+ const px = centerX + distance * Math.cos(angle);
+ const py = centerY + distance * Math.sin(angle);
+
+ // Curve to process (same style as original)
+ const cx1 = centerX + (px - centerX) * 0.3 + (Math.random() - 0.5) * 40;
+ const cy1 = centerY + (py - centerY) * 0.3 + (Math.random() - 0.5) * 40;
+ const cx2 = centerX + (px - centerX) * 0.7 + (Math.random() - 0.5) * 40;
+ const cy2 = centerY + (py - centerY) * 0.7 + (Math.random() - 0.5) * 40;
+
+ const path = `M${centerX},${centerY} C${cx1},${cy1} ${cx2},${cy2} ${px},${py}`;
+
+ // Draw the line
+ svg.append("path")
+ .attr("d", path)
+ .attr("class", "process-line")
+ .attr("stroke", "#222") // Same color as Bezier curves
+ .attr("stroke-width", 0.4) // Same thickness as Bezier curves
+ .attr("opacity", 0.05 + Math.random() * 0.03) // Same opacity as Bezier curves
+ .attr("fill", "none");
+
+ // Add gray circle at the end of the line (like in drawProcessKernelMap)
+ svg.append("circle")
+ .attr("cx", px)
+ .attr("cy", py)
+ .attr("r", 1)
+ .attr("fill", "#888")
+ .attr("stroke", "#555")
+ .attr("stroke-width", 0.5);
+ });
+ })
+ .catch(error => {
+ console.error('Error fetching processes:', error);
+ });
+}
+
// Draw curves at bottom
function drawLowerBezierGrid(num = 90) {
const width = window.innerWidth;
diff --git a/static/js/right-semicircle-menu.js b/static/js/right-semicircle-menu.js
index 4972a10..bd73e82 100644
--- a/static/js/right-semicircle-menu.js
+++ b/static/js/right-semicircle-menu.js
@@ -74,9 +74,13 @@ class RightSemicircleMenuManager {
.on('click', () => this.handleMenuClick(item.id))
.on('mouseenter', function() {
d3.select(this).style('fill', '#444');
+ // Show tooltip
+ showTooltip('in development', itemX, itemY);
})
.on('mouseleave', function() {
d3.select(this).style('fill', '#333');
+ // Hide tooltip
+ hideTooltip();
});
// SVG icon inside the circle
@@ -205,3 +209,53 @@ class RightSemicircleMenuManager {
this.isVisible = false;
}
}
+<<<<<<< HEAD
+=======
+
+/**
+ * Show tooltip with text
+ */
+function showTooltip(text, x, y) {
+ // Remove existing tooltip
+ d3.selectAll('.tooltip').remove();
+
+ // Create tooltip
+ const tooltip = d3.select('body')
+ .append('div')
+ .attr('class', 'tooltip')
+ .style('position', 'absolute')
+ .style('background', 'rgba(0, 0, 0, 0.8)')
+ .style('color', 'white')
+ .style('padding', '6px 10px')
+ .style('border-radius', '4px')
+ .style('font-size', '12px')
+ .style('font-family', "'Share Tech Mono', monospace")
+ .style('pointer-events', 'none')
+ .style('z-index', '1000')
+ .style('opacity', 0)
+ .text(text);
+
+ // Position tooltip
+ const tooltipWidth = tooltip.node().offsetWidth;
+ const tooltipHeight = tooltip.node().offsetHeight;
+ const offsetX = 10;
+ const offsetY = -tooltipHeight - 10;
+
+ tooltip
+ .style('left', (x + offsetX) + 'px')
+ .style('top', (y + offsetY) + 'px')
+ .transition()
+ .duration(200)
+ .style('opacity', 1);
+}
+
+/**
+ * Hide tooltip
+ */
+function hideTooltip() {
+ d3.selectAll('.tooltip')
+ .transition()
+ .duration(200)
+ .style('opacity', 0)
+ .remove();
+}
diff --git a/static/js/semicircle-menu.js b/static/js/semicircle-menu.js
new file mode 100755
index 0000000..260a6fd
--- /dev/null
+++ b/static/js/semicircle-menu.js
@@ -0,0 +1,179 @@
+// Semicircle Menu Manager - Right Side (as per example)
+class SemicircleMenuManager {
+ constructor() {
+ console.log("SemicircleMenuManager: constructor called");
+ this.menuItems = [
+ { id: 'processes', icon: '⚙️', label: 'Processes' },
+ { id: 'network', icon: '🌐', label: 'Network' },
+ { id: 'files', icon: '📁', label: 'Files' },
+ { id: 'system', icon: '🔧', label: 'System' },
+ { id: 'logs', icon: '📊', label: 'Logs' }
+ ];
+ this.isVisible = false;
+ }
+
+ renderSemicircleMenu() {
+ const svg = d3.select('svg');
+
+ // Remove existing menu elements
+ svg.selectAll('.semicircle-menu, .menu-item, .menu-bar, .menu-icon, .menu-label, .individual-menu-bar, .individual-menu-label').remove();
+
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+
+ // Semicircle menu position (LEFT side, larger radius like example)
+ const menuRadius = height * 0.4; // Larger radius - 40% of screen height
+ const menuX = -menuRadius * 0.3; // Shifted left (negative X)
+ const menuY = height / 2; // Centered vertically
+
+ console.log(`Menu dimensions: radius=${menuRadius}, x=${menuX}, y=${menuY}`);
+
+ // Create semicircle background - trimmed on right edge
+ const trimRight = menuRadius * 0.2; // Trim 20% from right edge
+ const semicirclePath = `M ${menuX} ${menuY - menuRadius}
+ A ${menuRadius} ${menuRadius} 0 0 1 ${menuX + menuRadius - trimRight} ${menuY + menuRadius}
+ L ${menuX + menuRadius - trimRight} ${menuY + menuRadius}
+ L ${menuX} ${menuY - menuRadius}`;
+
+ svg.append('path')
+ .attr('d', semicirclePath)
+ .attr('class', 'semicircle-menu')
+ .style('fill', 'rgba(255, 255, 255, 0.04)') // Force CSS style
+ .style('stroke', '#aaa')
+ .style('stroke-width', '0.5px')
+ .attr('opacity', 1);
+
+ // Removed horizontal bar with "Menu" text
+
+ // Create 5 menu items (circles) arranged in semicircle like original
+ this.menuItems.forEach((item, index) => {
+ // Spread across semicircle (180 degrees, left side) - like original positioning
+ const angle = Math.PI + (index * Math.PI / 4); // Start from left, go to right
+ const itemRadius = menuRadius * 0.12; // Restored to previous size
+ const itemDistance = menuRadius * 0.75; // 75% from center
+
+ const itemX = menuX + Math.cos(angle) * itemDistance;
+ const itemY = menuY + Math.sin(angle) * itemDistance;
+
+ console.log(`Menu item ${index}: angle=${angle}, x=${itemX}, y=${itemY}, radius=${itemRadius}`);
+
+ // Menu item circle
+ svg.append('circle')
+ .attr('cx', itemX)
+ .attr('cy', itemY)
+ .attr('r', itemRadius)
+ .attr('class', 'menu-item')
+ .style('fill', 'rgba(255, 255, 255, 0.04)') // Force CSS style
+ .style('stroke', '#aaa')
+ .style('stroke-width', '0.5px')
+ .attr('cursor', 'pointer')
+ .on('mouseover', function() {
+ d3.select(this)
+ .attr('fill', '#888')
+ .attr('stroke', '#aaa')
+ .attr('r', itemRadius * 1.1); // Slight scale on hover
+ })
+ .on('mouseout', function() {
+ d3.select(this)
+ .style('fill', 'rgba(255, 255, 255, 0.04)') // Reset to original color
+ .style('stroke', '#aaa')
+ .attr('r', itemRadius);
+ })
+ .on('click', () => this.handleMenuClick(item.id));
+
+ // Menu item icon
+ svg.append('text')
+ .attr('x', itemX)
+ .attr('y', itemY + itemRadius * 0.3)
+ .attr('class', 'menu-icon')
+ .style('fill', '#444') // Force CSS style
+ .style('font-family', "'Share Tech Mono', monospace")
+ .attr('text-anchor', 'middle')
+ .attr('font-size', `${itemRadius * 0.8}px`)
+ .text(item.icon);
+
+ // Individual menu bar for each circle (pointing to the right)
+ const individualBarWidth = menuRadius * 0.3; // 30% of radius
+ const individualBarHeight = menuRadius * 0.08; // 8% of radius
+
+ svg.append('rect')
+ .attr('x', itemX + itemRadius) // Start from right edge of circle
+ .attr('y', itemY - individualBarHeight/2)
+ .attr('width', individualBarWidth)
+ .attr('height', individualBarHeight)
+ .attr('class', 'individual-menu-bar')
+ .style('fill', 'rgba(255, 255, 255, 0.04)') // Force CSS style
+ .style('stroke', '#aaa')
+ .style('stroke-width', '0.5px')
+ .attr('opacity', 1)
+ .attr('rx', individualBarHeight/4);
+
+ // Individual menu label for each circle
+ svg.append('text')
+ .attr('x', itemX + itemRadius + individualBarWidth/2)
+ .attr('y', itemY + individualBarHeight * 0.3)
+ .attr('class', 'individual-menu-label')
+ .style('fill', '#444') // Force CSS style
+ .style('font-family', "'Share Tech Mono', monospace")
+ .attr('text-anchor', 'middle')
+ .attr('font-size', `${individualBarHeight * 0.6}px`)
+ .text(item.label);
+ });
+
+ // Removed "Menu" label
+
+ this.isVisible = true;
+ console.log("SemicircleMenuManager: left-side large menu rendered");
+ }
+
+ handleMenuClick(itemId) {
+ console.log(`SemicircleMenuManager: clicked ${itemId}`);
+
+ // Add visual feedback
+ const svg = d3.select('svg');
+ svg.selectAll('.menu-item')
+ .filter(function() {
+ return d3.select(this).datum() === itemId;
+ })
+ .transition()
+ .duration(200)
+ .attr('fill', '#aaa')
+ .transition()
+ .duration(200)
+ .attr('fill', '#666');
+
+ // Handle different menu actions
+ switch(itemId) {
+ case 'processes':
+ console.log('Switching to processes view');
+ break;
+ case 'network':
+ console.log('Switching to network view');
+ break;
+ case 'files':
+ console.log('Switching to files view');
+ break;
+ case 'system':
+ console.log('Switching to system view');
+ break;
+ case 'logs':
+ console.log('Switching to logs view');
+ break;
+ }
+ }
+
+ toggleMenu() {
+ if (this.isVisible) {
+ this.hideMenu();
+ } else {
+ this.renderSemicircleMenu();
+ }
+ }
+
+ hideMenu() {
+ const svg = d3.select('svg');
+ svg.selectAll('.semicircle-menu, .menu-item, .menu-bar, .menu-icon, .menu-label, .individual-menu-bar, .individual-menu-label').remove();
+ this.isVisible = false;
+ console.log("SemicircleMenuManager: menu hidden");
+ }
+}