From 2e7205a12c68c6c6f85064b41932ede04378987d Mon Sep 17 00:00:00 2001 From: Aleksei Fedorov Date: Sun, 15 Mar 2026 18:42:00 +0000 Subject: [PATCH] file system --- app.py | 14 +- index.html | 4 +- static/js/crypto-belt.js | 24 +- static/js/filesystem-map.js | 354 ++++++++++++++++++++++---- templates/linux-crypto-subsystem.html | 2 +- 5 files changed, 339 insertions(+), 59 deletions(-) diff --git a/app.py b/app.py index d8238c4..c7b4545 100644 --- a/app.py +++ b/app.py @@ -1545,6 +1545,10 @@ def get_filesystem_blocks(): zone_writing = int(round(writing_blocks_total * zone_write_share)) zone_writing = max(0, min(zone_used, zone_writing)) zone_writing_total += zone_writing + inode_pressure = int(max(0, min( + 100, + round((z["activity"] * 2.6) + (zone_writing * 0.9) + (local_used_ratio * 38.0)) + ))) cell_index = 0 for r in range(row_start, row_end + 1): @@ -1578,7 +1582,8 @@ def get_filesystem_blocks(): "row_end": row_end, "activity": int(z["activity"]), "used_percent": round(local_used_ratio * 100.0, 1), - "writing_blocks": zone_writing + "writing_blocks": zone_writing, + "inode_pressure": inode_pressure }) writing_blocks = sum(1 for b in blocks if b["state"] == "writing") @@ -1586,6 +1591,10 @@ def get_filesystem_blocks(): FILESYSTEM_PREV["timestamp"] = now FILESYSTEM_PREV["write_bytes"] = write_bytes + inode_pressure_global = 0 + if zones: + inode_pressure_global = int(round(sum(int(z.get("inode_pressure", 0)) for z in zones) / len(zones))) + return { "timestamp": datetime.now().isoformat(), "rows": rows, @@ -1598,7 +1607,8 @@ def get_filesystem_blocks(): "free_gb": free_gb, "used_percent": round(used_percent, 2), "write_bps": round(write_bps, 2), - "writing_blocks": writing_blocks + "writing_blocks": writing_blocks, + "inode_pressure": inode_pressure_global } } diff --git a/index.html b/index.html index 5853c5c..2956112 100755 --- a/index.html +++ b/index.html @@ -100,8 +100,8 @@

Linux Kernel Ring 0 Visualization

- - + + diff --git a/static/js/crypto-belt.js b/static/js/crypto-belt.js index 0a9377c..807dea1 100644 --- a/static/js/crypto-belt.js +++ b/static/js/crypto-belt.js @@ -1092,10 +1092,11 @@ class CryptoSubsystemVisualization { const panelX = Math.floor(width * 0.41) + 10; const maxSafeW = Math.max(300, rightColumnX - panelX - 16); const panelW = Math.max(330, Math.min(Math.min(470, Math.floor(width * 0.3)), maxSafeW)); - const panelH = 238; const decisionPanelY = Math.max(450, Math.floor(height * 0.52)); const materialCardH = 226; - const panelY = Math.min(height - panelH - 22, decisionPanelY + materialCardH + 16); + const panelY = decisionPanelY + materialCardH + 16; + const maxPanelH = Math.max(120, height - panelY - 22); + const panelH = Math.min(238, maxPanelH); const panel = layer.append('g').attr('class', 'crypto-entropy-cloud'); panel.append('rect') @@ -1146,7 +1147,7 @@ class CryptoSubsystemVisualization { const cloudY = panelY + 86; // Keep particle viewport compact so right-side source metrics fit comfortably. const cloudW = Math.max(130, panelW - 220); - const cloudH = panelH - 100; + const cloudH = Math.max(60, panelH - 102); panel.append('rect') .attr('x', cloudX) .attr('y', cloudY) @@ -1178,7 +1179,8 @@ class CryptoSubsystemVisualization { const keyRate = Number(entropy.key_birth_rate_est || 0); const keyNodes = Math.max(1, Math.min(5, Math.round(keyRate / 2.2))); const keyBaseX = panelX + panelW - 110; - const keyBaseY = panelY + 112; + const srcY = panelY + panelH - 66; + const keyBaseY = srcY - 28; panel.append('text') .attr('x', keyBaseX) .attr('y', keyBaseY - 12) @@ -1210,7 +1212,6 @@ class CryptoSubsystemVisualization { .style('stroke-opacity', 0.7); const srcX = panelX + panelW - 158; - const srcY = panelY + 152; const sourceBarX = srcX + 70; const sourceBarW = Math.max(64, Math.min(76, panelX + panelW - sourceBarX - 8)); panel.append('text') @@ -1274,8 +1275,9 @@ class CryptoSubsystemVisualization { const queueH = 86; const offloadH = 116; const totalH = clientsH + queueH + offloadH + (gap * 2); - // Keep stage-1 HUD fully visible even on shorter viewports. - const baseY = Math.max(180, height - totalH - 20); + // Align left stage stack with Algorithm Material Card top level. + const decisionPanelY = Math.max(450, Math.floor(height * 0.52)); + const baseY = Math.min(decisionPanelY, Math.max(180, height - totalH - 20)); const isAllSelected = this.selectedClientFilters.size === 0; const drawPanelShell = (x, y, w, h, title) => { @@ -1489,9 +1491,9 @@ class CryptoSubsystemVisualization { } drawNode(group, x, y, label, level, intensity, palette, emphasis) { - const width = Math.min(Math.max(150, String(label).length * 8 + 28), 250); - const height = 34; - const radius = 8; + const width = Math.min(Math.max(132, String(label).length * 7 + 24), 220); + const height = 30; + const radius = 7; const lineColor = emphasis ? palette.accent : (intensity > 1.2 ? palette.accent : palette.stroke); const fillColor = level === 'crypto' ? '#11161f' : palette.fill; @@ -1513,7 +1515,7 @@ class CryptoSubsystemVisualization { .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('font-family', 'Share Tech Mono, monospace') - .style('font-size', '11px') + .style('font-size', '10px') .style('letter-spacing', '0.35px') .style('fill', emphasis ? '#ffffff' : '#eef3fb') .text(String(label).toUpperCase()); diff --git a/static/js/filesystem-map.js b/static/js/filesystem-map.js index 0e6ff33..8dc09a5 100644 --- a/static/js/filesystem-map.js +++ b/static/js/filesystem-map.js @@ -1,7 +1,7 @@ // Filesystem Block Map Visualization -// Version: 2 +// Version: 3 -debugLog('🗂️ filesystem-map.js v2: Script loading...'); +debugLog('🗂️ filesystem-map.js v3: Script loading...'); class FilesystemMapVisualization { constructor() { @@ -15,6 +15,12 @@ class FilesystemMapVisualization { this.overlayNodes = []; this.resizeHandler = null; this.telemetry = null; + this.tick = 0; + this.renderMode = 'occupancy'; + this.modeButtons = new Map(); + this.legendNode = null; + this.orbHitArea = null; + this.canvasClickHandler = null; } init(containerId = 'filesystem-map-container') { @@ -29,7 +35,7 @@ class FilesystemMapVisualization { inset: 0; width: 100%; height: 100%; - background: #0E1114; + background: #090d14; z-index: 9999; display: none; visibility: hidden; @@ -48,6 +54,8 @@ class FilesystemMapVisualization { `; this.container.appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); + this.canvasClickHandler = (event) => this.onCanvasClick(event); + this.canvas.addEventListener('click', this.canvasClickHandler); this.createOverlayUI(); this.addExitButton(); @@ -71,7 +79,7 @@ class FilesystemMapVisualization { letter-spacing: 1px; z-index: 1001; `; - title.textContent = 'FILESYSTEM BLOCK MAP'; + title.textContent = 'FILESYSTEM INDEX ARCHIVE (in development)'; this.container.appendChild(title); this.overlayNodes.push(title); @@ -86,9 +94,46 @@ class FilesystemMapVisualization { font-size: 11px; z-index: 1001; `; - legend.textContent = 'filesystem map by zones: gray=free, black=used, yellow=actively writing'; + legend.textContent = 'filesystem packages + radial block index: blue=used, cyan=writing, dark=free'; this.container.appendChild(legend); this.overlayNodes.push(legend); + this.legendNode = legend; + + const modePanel = document.createElement('div'); + modePanel.style.cssText = ` + position: absolute; + top: 72px; + right: 22px; + display: flex; + gap: 6px; + z-index: 1001; + `; + const modes = [ + { key: 'occupancy', label: 'OCCUPANCY' }, + { key: 'write', label: 'WRITE HEAT' }, + { key: 'inode', label: 'INODE' } + ]; + modes.forEach((item) => { + const btn = document.createElement('button'); + btn.textContent = item.label; + btn.style.cssText = ` + padding: 4px 8px; + background: rgba(12, 18, 28, 0.9); + border: 1px solid rgba(130, 148, 172, 0.35); + color: #9fb0c8; + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + letter-spacing: 0.3px; + cursor: pointer; + `; + btn.onclick = () => this.setRenderMode(item.key); + modePanel.appendChild(btn); + this.modeButtons.set(item.key, btn); + this.overlayNodes.push(btn); + }); + this.container.appendChild(modePanel); + this.overlayNodes.push(modePanel); + this.setRenderMode(this.renderMode); const info = document.createElement('div'); info.style.cssText = ` @@ -161,7 +206,8 @@ class FilesystemMapVisualization { `used ${m.used_gb || 0}GB / ${m.total_gb || 0}GB (${m.used_percent || 0}%)` + ` | free ${m.free_gb || 0}GB` + ` | write ${(m.write_bps || 0).toFixed(0)} B/s` + - ` | active blocks ${m.writing_blocks || 0}`; + ` | writing blocks ${m.writing_blocks || 0}` + + ` | inode ${m.inode_pressure || 0}%`; } }) .catch((err) => { @@ -171,13 +217,129 @@ class FilesystemMapVisualization { }); } - drawGrid() { + drawRoundedRect(x, y, w, h, r) { + const ctx = this.ctx; + const radius = Math.max(0, Math.min(r, w / 2, h / 2)); + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + w - radius, y); + ctx.quadraticCurveTo(x + w, y, x + w, y + radius); + ctx.lineTo(x + w, y + h - radius); + ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h); + ctx.lineTo(x + radius, y + h); + ctx.quadraticCurveTo(x, y + h, x, y + h - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + } + + drawPackageStack(x, y, w, h, layers, accentColor, label, metaText) { + const ctx = this.ctx; + const stackLayers = Math.max(2, Math.min(4, layers)); + for (let i = stackLayers - 1; i >= 0; i -= 1) { + const dx = -i * 4; + const dy = i * 3; + this.drawRoundedRect(x + dx, y + dy, w, h, 5); + ctx.fillStyle = i === 0 ? 'rgba(14, 24, 36, 0.95)' : 'rgba(10, 16, 26, 0.85)'; + ctx.fill(); + ctx.strokeStyle = i === 0 ? accentColor : 'rgba(72, 94, 124, 0.35)'; + ctx.lineWidth = i === 0 ? 1.0 : 0.7; + ctx.stroke(); + } + ctx.fillStyle = '#9fc0e8'; + ctx.font = '9px "Share Tech Mono", monospace'; + ctx.fillText(label, x + 8, y + 13); + ctx.fillStyle = 'rgba(122, 141, 167, 0.85)'; + ctx.font = '8px "Share Tech Mono", monospace'; + ctx.fillText(metaText, x + 8, y + 24); + } + + drawParticleOrb(x, y, radius, meta) { + const ctx = this.ctx; + const mode = String(this.renderMode || 'occupancy'); + const inodePressure = Math.max(0, Math.min(100, Number(meta?.inode_pressure || 0))); + const writeBps = Math.max(0, Number(meta?.write_bps || 0)); + const writeNorm = Math.max(0, Math.min(1, writeBps / (300 * 1024 * 1024))); + const modeBoost = mode === 'write' ? 0.25 : (mode === 'inode' ? 0.18 : 0.08); + const pulse = 0.45 + 0.55 * ((Math.sin(this.tick * 0.06) + 1) / 2); + const glow = Math.max(0.1, Math.min(1.0, (inodePressure / 100) * 0.6 + writeNorm * 0.4 + modeBoost)); + + // Outer glow + const g = ctx.createRadialGradient(x, y, radius * 0.3, x, y, radius * 1.2); + g.addColorStop(0, `rgba(145, 222, 255, ${0.12 + glow * 0.25})`); + g.addColorStop(1, 'rgba(145, 222, 255, 0)'); + ctx.beginPath(); + ctx.arc(x, y, radius * 1.2, 0, Math.PI * 2); + ctx.fillStyle = g; + ctx.fill(); + + // Orb body + const orb = ctx.createRadialGradient(x - radius * 0.25, y - radius * 0.35, radius * 0.2, x, y, radius); + orb.addColorStop(0, 'rgba(196, 238, 255, 0.85)'); + orb.addColorStop(0.45, 'rgba(79, 148, 196, 0.46)'); + orb.addColorStop(1, 'rgba(11, 22, 36, 0.95)'); + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.fillStyle = orb; + ctx.fill(); + + // Rim + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.strokeStyle = `rgba(137, 208, 245, ${0.58 + pulse * 0.22})`; + ctx.lineWidth = 1.2; + ctx.stroke(); + + // Minimal version: no inner streaks and no labels. + } + + setRenderMode(modeKey) { + this.renderMode = ['occupancy', 'write', 'inode'].includes(modeKey) ? modeKey : 'occupancy'; + this.modeButtons.forEach((btn, key) => { + const active = key === this.renderMode; + btn.style.background = active ? 'rgba(34, 58, 88, 0.92)' : 'rgba(12, 18, 28, 0.9)'; + btn.style.borderColor = active ? 'rgba(129, 180, 255, 0.9)' : 'rgba(130, 148, 172, 0.35)'; + btn.style.color = active ? '#d3e7ff' : '#9fb0c8'; + }); + if (this.legendNode) { + if (this.renderMode === 'write') { + this.legendNode.textContent = 'mode: write heat (cyan=write hot, blue=active, dark=cold)'; + } else if (this.renderMode === 'inode') { + this.legendNode.textContent = 'mode: inode pressure (cyan=high inode pressure)'; + } else { + this.legendNode.textContent = 'mode: occupancy (blue=used, cyan=writing, dark=free)'; + } + } + } + + cycleRenderMode() { + const order = ['occupancy', 'write', 'inode']; + const idx = order.indexOf(this.renderMode); + const next = order[(idx + 1) % order.length]; + this.setRenderMode(next); + } + + onCanvasClick(event) { + if (!this.orbHitArea || !this.canvas) return; + const rect = this.canvas.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + const dx = x - this.orbHitArea.x; + const dy = y - this.orbHitArea.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist <= this.orbHitArea.r * 1.25) { + this.cycleRenderMode(); + } + } + + drawScene() { if (!this.ctx || !this.canvas) return; const w = window.innerWidth; const h = window.innerHeight; this.ctx.clearRect(0, 0, w, h); + this.tick += 1; - this.ctx.fillStyle = '#0E1114'; + this.ctx.fillStyle = '#090d14'; this.ctx.fillRect(0, 0, w, h); if (!this.telemetry || !Array.isArray(this.telemetry.blocks)) return; @@ -186,56 +348,162 @@ class FilesystemMapVisualization { const cols = Math.max(1, Number(this.telemetry.cols || 1)); const blocks = this.telemetry.blocks; const zones = Array.isArray(this.telemetry.zones) ? this.telemetry.zones : []; + const zoneMap = new Map(zones.map((z) => [String(z.id || ''), z])); - const topPad = 120; - const bottomPad = 40; - const sidePad = 70; - const availableW = Math.max(100, w - sidePad * 2); - const availableH = Math.max(100, h - topPad - bottomPad); + const cx = Math.floor(w * 0.53); + const cy = Math.floor(h * 0.63); + const outerR = Math.max(130, Math.min(250, Math.floor(Math.min(w, h) * 0.28))); + const innerR = Math.max(24, Math.floor(outerR * 0.14)); + const ringGap = (outerR - innerR) / Math.max(1, rows); + const angleStep = (Math.PI * 2) / Math.max(1, cols); - const cellW = Math.floor(availableW / cols); - const cellH = Math.floor(availableH / rows); - const size = Math.max(4, Math.min(cellW, cellH) - 2); - const startX = Math.floor((w - (cols * (size + 2))) / 2); - const startY = Math.floor(topPad + (availableH - (rows * (size + 2))) / 2); + // Background circuit lines. + this.ctx.strokeStyle = 'rgba(150, 38, 64, 0.17)'; + this.ctx.lineWidth = 1; + for (let i = 0; i < 9; i += 1) { + const y = 120 + i * 44; + this.ctx.beginPath(); + this.ctx.moveTo(cx - outerR - 130, y); + this.ctx.lineTo(cx + outerR + 130, y); + this.ctx.stroke(); + } + // Main rings. + for (let i = 0; i <= 7; i += 1) { + const r = innerR + ((outerR - innerR) * i / 7); + this.ctx.beginPath(); + this.ctx.arc(cx, cy, r, 0, Math.PI * 2); + this.ctx.strokeStyle = i % 2 === 0 ? 'rgba(89, 140, 192, 0.22)' : 'rgba(58, 96, 136, 0.16)'; + this.ctx.lineWidth = i === 7 ? 1.2 : 0.8; + this.ctx.stroke(); + } + + // Radial block matrix. blocks.forEach((b) => { - const r = Number.isFinite(Number(b.r)) ? Number(b.r) : Math.floor(Number(b.i || 0) / cols); - const c = Number.isFinite(Number(b.c)) ? Number(b.c) : (Number(b.i || 0) % cols); - const x = startX + c * (size + 2); - const y = startY + r * (size + 2); + const rIdx = Number.isFinite(Number(b.r)) ? Number(b.r) : Math.floor(Number(b.i || 0) / cols); + const cIdx = Number.isFinite(Number(b.c)) ? Number(b.c) : (Number(b.i || 0) % cols); + const r0 = innerR + rIdx * ringGap; + const r1 = r0 + Math.max(1.3, ringGap * 0.8); + const a0 = cIdx * angleStep; + const a1 = a0 + angleStep * 0.88; - if (b.state === 'free') this.ctx.fillStyle = '#8f9499'; - else if (b.state === 'writing') this.ctx.fillStyle = '#e6c15a'; - else this.ctx.fillStyle = '#15171a'; + const z = zoneMap.get(String(b.zone_id || '')) || {}; + const zoneAct = Math.max(0, Math.min(100, Number(z.activity || 0))); + const inodePressure = Math.max(0, Math.min(100, Number(z.inode_pressure || 0))); + if (this.renderMode === 'write') { + if (b.state === 'writing') this.ctx.fillStyle = 'rgba(127, 232, 255, 0.92)'; + else if (b.state === 'used') this.ctx.fillStyle = `rgba(44, 132, 184, ${0.32 + Math.min(0.5, zoneAct / 120)})`; + else this.ctx.fillStyle = 'rgba(14, 28, 44, 0.48)'; + } else if (this.renderMode === 'inode') { + if (b.state === 'free') this.ctx.fillStyle = 'rgba(12, 24, 38, 0.45)'; + else this.ctx.fillStyle = `rgba(127, 232, 255, ${0.18 + Math.min(0.72, inodePressure / 110)})`; + } else { + if (b.state === 'writing') this.ctx.fillStyle = 'rgba(127, 232, 255, 0.9)'; + else if (b.state === 'free') this.ctx.fillStyle = 'rgba(19, 38, 58, 0.55)'; + else this.ctx.fillStyle = 'rgba(44, 132, 184, 0.72)'; + } - this.ctx.fillRect(x, y, size, size); + this.ctx.beginPath(); + this.ctx.arc(cx, cy, r1, a0, a1); + this.ctx.arc(cx, cy, r0, a1, a0, true); + this.ctx.closePath(); + this.ctx.fill(); }); - // Draw zone boundaries and labels to make a readable filesystem map. - this.ctx.font = '11px "Share Tech Mono", monospace'; - zones.forEach((z) => { - const rs = Number(z.row_start || 0); - const re = Number(z.row_end || rs); - const zx = startX - 6; - const zy = startY + rs * (size + 2) - 4; - const zw = cols * (size + 2) + 8; - const zh = (re - rs + 1) * (size + 2) + 6; - - this.ctx.strokeStyle = 'rgba(170, 178, 188, 0.26)'; + // Central hole. + this.ctx.beginPath(); + this.ctx.arc(cx, cy, innerR - 3, 0, Math.PI * 2); + this.ctx.fillStyle = '#081018'; + this.ctx.fill(); + + // Alert banner. + const m = this.telemetry.meta || {}; + if (Number(m.writing_blocks || 0) > 0) { + const pulse = 0.5 + 0.5 * Math.sin(this.tick * 0.08); + const bw = 330; + const bh = 40; + const bx = cx - Math.floor(bw / 2); + const by = cy - outerR - 26; + this.drawRoundedRect(bx, by, bw, bh, 5); + this.ctx.fillStyle = `rgba(${90 + Math.floor(80 * pulse)}, 20, 20, 0.9)`; + this.ctx.fill(); + this.ctx.strokeStyle = 'rgba(220, 90, 90, 0.85)'; this.ctx.lineWidth = 1; - this.ctx.strokeRect(zx, zy, zw, zh); + this.ctx.stroke(); + this.ctx.fillStyle = '#ffd8c0'; + this.ctx.font = 'bold 17px "Share Tech Mono", monospace'; + this.ctx.fillText('DATA PACKAGE WRITING', bx + 18, by + 26); + } - const label = `${z.name} used:${z.used_percent}% act:${z.activity}`; - this.ctx.fillStyle = 'rgba(200, 206, 216, 0.92)'; - this.ctx.fillText(label, zx + 8, zy + 14); - }); + // Side package stacks from zones. + const zoneItems = zones.slice(0, 14); + const leftItems = zoneItems.filter((_, i) => i % 2 === 0); + const rightItems = zoneItems.filter((_, i) => i % 2 === 1); + const cardW = 90; + const cardH = 32; + const leftX = Math.max(24, cx - outerR - 220); + const rightX = Math.min(w - cardW - 24, cx + outerR + 110); + const topY = Math.max(150, cy - outerR + 12); + + const drawZonePack = (list, xBase, isLeft) => { + list.forEach((z, idx) => { + const y = topY + idx * 42; + const active = Number(z.activity || 0) > 6 || Number(z.writing_blocks || 0) > 0; + const accent = active ? 'rgba(118, 220, 255, 0.92)' : 'rgba(90, 132, 178, 0.7)'; + const layers = 2 + (active ? 2 : 1); + this.drawPackageStack( + xBase, + y, + cardW, + cardH, + layers, + accent, + String(z.name || z.id || 'zone'), + `used:${Number(z.used_percent || 0).toFixed(0)}%` + ); + + // Connector to central disk. + const sx = isLeft ? xBase + cardW : xBase; + const sy = y + cardH / 2; + const tx = cx + (isLeft ? -outerR * 0.85 : outerR * 0.85); + const ty = cy - outerR * 0.65 + idx * 10; + this.ctx.beginPath(); + this.ctx.moveTo(sx, sy); + this.ctx.bezierCurveTo( + sx + (isLeft ? 60 : -60), + sy, + tx + (isLeft ? -40 : 40), + ty, + tx, + ty + ); + this.ctx.strokeStyle = 'rgba(169, 52, 80, 0.26)'; + this.ctx.lineWidth = 1; + this.ctx.stroke(); + }); + }; + + drawZonePack(leftItems, leftX, true); + drawZonePack(rightItems, rightX, false); + + // Mini particle-view orb (reference-style status widget). + const orbX = Math.max(84, leftX + 44); + const orbY = Math.min(h - 48, topY + 290); + this.drawParticleOrb(orbX, orbY, 42, m); + this.orbHitArea = { x: orbX, y: orbY, r: 42 }; + + // Minimal host-data labels. + this.ctx.fillStyle = '#6fa4cf'; + this.ctx.font = '10px "Share Tech Mono", monospace'; + this.ctx.fillText(`HOST/DATA FILE ${(m.used_gb || 0).toFixed(0)}GB`, leftX, topY - 26); + this.ctx.fillText(`HOST/DATA FILE ${(m.free_gb || 0).toFixed(0)}GB`, leftX, topY - 10); + this.ctx.fillText(`HOST/DATA WR ${(m.write_bps || 0).toFixed(0)} B/s`, rightX - 40, topY - 10); } animate() { if (!this.isActive) return; this.animationId = requestAnimationFrame(() => this.animate()); - this.drawGrid(); + this.drawScene(); } activate() { diff --git a/templates/linux-crypto-subsystem.html b/templates/linux-crypto-subsystem.html index 9ea400f..582c756 100644 --- a/templates/linux-crypto-subsystem.html +++ b/templates/linux-crypto-subsystem.html @@ -135,7 +135,7 @@

Linux Crypto Kernel Visualization

- +