<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nvline</title> <style> body { background-color: #121212; color: #ffffff; font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; padding: 20px; box-sizing: border-box; } h1 { margin-bottom: 10px; font-size: 24px; } #chart-container { width: 90%; height: 80%; } #chart { width: 100%; height: 100%; } .axis-label { fill: #ffffff; } .tooltip { position: absolute; background: rgba(0, 0, 0, 0.7); color: #fff; padding: 5px; border-radius: 3px; pointer-events: none; font-size: 12px; } .grid line { stroke: #444; } .grid path { stroke: none; } input[type="file"] { display: none; } .custom-file-upload { border: 1px solid #ccc; display: inline-block; padding: 6px 12px; cursor: pointer; } </style> </head> <body> <h1>Import nvline JSONL File and Display Memory Usage</h1> <label for="fileInput" class="custom-file-upload"> Add nvline JSONL File </label> <input type="file" id="fileInput" accept=".jsonl"> <div id="chart-container"> <svg id="chart"></svg> </div> <div id="tooltip" class="tooltip" style="display: none;"></div> <script> document.getElementById('fileInput').addEventListener('change', handleFile, false); function handleFile(event) { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = function (event) { const lines = event.target.result.split('\n').filter(line => line.trim() !== ''); const data = lines.map(line => JSON.parse(line)); drawChart(data); }; reader.readAsText(file); } function drawChart(data) { const svg = document.getElementById('chart'); svg.innerHTML = ''; const width = svg.clientWidth; const height = svg.clientHeight; const padding = 30; const xScale = width / (data.length - 1); const yMax = Math.max(...data.map(d => d.memory_total)); const yScale = (height - padding * 2) / yMax; const line = (points) => points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0]},${p[1]}`).join(' '); const memoryUsedPoints = data.map((d, i) => [i * xScale, height - padding - d.memory_used * yScale]); const memoryFreePoints = data.map((d, i) => [i * xScale, height - padding - d.memory_free * yScale]); const memoryTotalPoints = data.map((d, i) => [i * xScale, height - padding - d.memory_total * yScale]); const gridX = document.createElementNS('http://www.w3.org/2000/svg', 'g'); const gridY = document.createElementNS('http://www.w3.org/2000/svg', 'g'); for (let i = 0; i < data.length; i++) { const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', i * xScale); line.setAttribute('y1', height - padding); line.setAttribute('x2', i * xScale); line.setAttribute('y2', padding); line.setAttribute('class', 'grid'); line.setAttribute('stroke', '#444'); gridX.appendChild(line); } for (let i = 0; i <= yMax; i += yMax / 10) { const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', 0); line.setAttribute('y1', height - padding - i * yScale); line.setAttribute('x2', width); line.setAttribute('y2', height - padding - i * yScale); line.setAttribute('class', 'grid'); line.setAttribute('stroke', '#444'); gridY.appendChild(line); } svg.appendChild(gridX); svg.appendChild(gridY); const pathUsed = document.createElementNS('http://www.w3.org/2000/svg', 'path'); pathUsed.setAttribute('d', line(memoryUsedPoints)); pathUsed.setAttribute('stroke', 'red'); pathUsed.setAttribute('fill', 'none'); svg.appendChild(pathUsed); const pathFree = document.createElementNS('http://www.w3.org/2000/svg', 'path'); pathFree.setAttribute('d', line(memoryFreePoints)); pathFree.setAttribute('stroke', 'green'); pathFree.setAttribute('fill', 'none'); svg.appendChild(pathFree); const pathTotal = document.createElementNS('http://www.w3.org/2000/svg', 'path'); pathTotal.setAttribute('d', line(memoryTotalPoints)); pathTotal.setAttribute('stroke', 'blue'); pathTotal.setAttribute('fill', 'none'); svg.appendChild(pathTotal); memoryUsedPoints.forEach((point, i) => { const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', point[0]); circle.setAttribute('cy', point[1]); circle.setAttribute('r', 3); circle.setAttribute('fill', 'red'); circle.setAttribute('class', 'dot dot-used'); circle.setAttribute('data-index', i); svg.appendChild(circle); }); memoryFreePoints.forEach((point, i) => { const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', point[0]); circle.setAttribute('cy', point[1]); circle.setAttribute('r', 3); circle.setAttribute('fill', 'green'); circle.setAttribute('class', 'dot dot-free'); circle.setAttribute('data-index', i); svg.appendChild(circle); }); memoryTotalPoints.forEach((point, i) => { const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', point[0]); circle.setAttribute('cy', point[1]); circle.setAttribute('r', 3); circle.setAttribute('fill', 'blue'); circle.setAttribute('class', 'dot dot-total'); circle.setAttribute('data-index', i); svg.appendChild(circle); }); const xAxisLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); xAxisLabel.setAttribute('x', width / 2); xAxisLabel.setAttribute('y', height - 10); xAxisLabel.setAttribute('class', 'axis-label'); xAxisLabel.setAttribute('text-anchor', 'middle'); xAxisLabel.textContent = 'Data Points'; svg.appendChild(xAxisLabel); const yAxisLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); yAxisLabel.setAttribute('x', -height / 2); yAxisLabel.setAttribute('y', 20); yAxisLabel.setAttribute('class', 'axis-label'); yAxisLabel.setAttribute('text-anchor', 'middle'); yAxisLabel.setAttribute('transform', 'rotate(-90)'); yAxisLabel.textContent = 'Memory Usage'; svg.appendChild(yAxisLabel); const tooltip = document.getElementById('tooltip'); svg.addEventListener('mousemove', (event) => { const rect = svg.getBoundingClientRect(); const x = event.clientX - rect.left; const index = Math.round(x / xScale); document.querySelectorAll('.dot').forEach(dot => dot.style.display = 'none'); if (index >= 0 && index < data.length) { const memoryUsed = data[index].memory_used; const memoryFree = data[index].memory_free; const memoryTotal = data[index].memory_total; const timestamp = new Date(1000 * data[index].timestamp).toISOString(); tooltip.style.display = 'block'; tooltip.style.left = event.pageX + 10 + 'px'; tooltip.style.top = event.pageY - 10 + 'px'; tooltip.innerHTML = `Total: ${memoryTotal} Used: ${memoryUsed}<br>Free: ${memoryFree}<br> Timestamp: ${timestamp}`; document.querySelector(`.dot-used[data-index='${index}']`).style.display = 'block'; document.querySelector(`.dot-free[data-index='${index}']`).style.display = 'block'; document.querySelector(`.dot-total[data-index='${index}']`).style.display = 'block'; } else { tooltip.style.display = 'none'; } }); svg.addEventListener('mouseout', () => { tooltip.style.display = 'none'; document.querySelectorAll('.dot').forEach(dot => dot.style.display = 'none'); }); } </script> </body> </html>