Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<title>Kernel AI</title>

<!-- SEO Meta Tags -->
<meta name="description" content="Kernel AI - Interactive Linux architecture visualization tool for students. Learn Linux kernel architecture, system calls, and process management through real-time visualizations. Educational tool for understanding Linux internals.">
<meta name="keywords" content="Kernel AI, Ring0, Linux architecture, Linux kernel architecture, Linux system architecture, Linux architecture visualization, Linux kernel internals, Linux architecture learning, interactive Linux architecture, Linux kernel components, system calls visualization, Linux architecture diagram, Linux learning tool, kernel architecture study">
<meta name="author" content="Aleksei Fedorov">
<meta name="robots" content="index, follow">

Expand Down
65 changes: 65 additions & 0 deletions static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down
54 changes: 54 additions & 0 deletions static/js/right-semicircle-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
179 changes: 179 additions & 0 deletions static/js/semicircle-menu.js
Original file line number Diff line number Diff line change
@@ -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");
}
}