Merge pull request #60 from lklynet/pr-38

Pr 38
This commit is contained in:
LKLY
2026-01-12 14:57:42 -05:00
committed by GitHub
3 changed files with 250 additions and 17 deletions
+163 -9
View File
@@ -89,6 +89,12 @@ const animate = () => {
const openDiagnostics = () => {
document.getElementById("diagnosticsModal").classList.add("active");
// Ensure bandwidth graph is correctly sized and drawn after modal opens
setTimeout(() => {
if (typeof resizeBandwidthCanvas === "function") {
resizeBandwidthCanvas();
}
}, 50);
};
const closeDiagnostics = () => {
@@ -587,7 +593,6 @@ terminalInput.addEventListener("keypress", async (e) => {
}
}
let scope = "GLOBAL";
let target = null;
@@ -686,6 +691,21 @@ terminalInput.addEventListener("keypress", async (e) => {
}
});
const formatBandwidth = (bytes, short = false) => {
const kb = bytes / 1024;
const mb = kb / 1024;
const gb = mb / 1024;
const space = short ? "" : " ";
if (gb >= 1) {
return gb.toFixed(short ? 1 : 2) + space + "GB";
} else if (mb >= 1) {
return mb.toFixed(short ? 1 : 2) + space + "MB";
} else {
return kb.toFixed(short ? 0 : 1) + space + "KB";
}
};
const evtSource = new EventSource("/events");
evtSource.onmessage = (event) => {
@@ -796,14 +816,19 @@ evtSource.onmessage = (event) => {
d.invalidPoW.toLocaleString();
document.getElementById("diag-invalid-sig").innerText =
d.invalidSig.toLocaleString();
document.getElementById("diag-bandwidth-in").innerText = formatBandwidth(
d.bytesReceived
);
document.getElementById("diag-bandwidth-out").innerText = formatBandwidth(
d.bytesRelayed
);
document.getElementById("diag-leave").innerText =
d.leaveMessages.toLocaleString();
if (typeof addBandwidthData === "function") {
addBandwidthData(d.bytesReceived, d.bytesRelayed);
drawBandwidthGraph();
document.getElementById("current-in").innerText = formatBandwidth(
d.bytesReceived
);
document.getElementById("current-out").innerText = formatBandwidth(
d.bytesRelayed
);
}
}
};
@@ -817,6 +842,135 @@ countEl.classList.add("loaded");
updateParticles(initialCount);
animate();
const bandwidthHistory = { timestamps: [], bytesIn: [], bytesOut: [] };
let selectedTimeRange = 300;
const bandwidthCanvas = document.getElementById("bandwidthGraph");
const bandwidthCtx = bandwidthCanvas ? bandwidthCanvas.getContext("2d") : null;
const bandwidthOverlay = document.getElementById("bandwidthOverlay");
function resizeBandwidthCanvas() {
if (!bandwidthCanvas) return;
const rect = bandwidthCanvas.getBoundingClientRect();
bandwidthCanvas.width = rect.width;
bandwidthCanvas.height = rect.height;
drawBandwidthGraph();
}
window.addEventListener("resize", resizeBandwidthCanvas);
setTimeout(resizeBandwidthCanvas, 100);
const toggleBandwidthGraph = () => {
if (!bandwidthOverlay) return;
bandwidthOverlay.classList.toggle("collapsed");
const closeBtn = document.querySelector(".bandwidth-overlay .close-btn");
if (closeBtn) {
closeBtn.textContent = bandwidthOverlay.classList.contains("collapsed")
? "+"
: "";
}
// Redraw after transition
setTimeout(resizeBandwidthCanvas, 350);
};
window.toggleBandwidthGraph = toggleBandwidthGraph;
const timePills = document.querySelectorAll(".time-pill");
timePills.forEach((pill) => {
pill.addEventListener("click", (e) => {
e.stopPropagation();
timePills.forEach((p) => p.classList.remove("active"));
pill.classList.add("active");
const value = pill.dataset.value;
selectedTimeRange = value === "all" ? "all" : parseInt(value);
drawBandwidthGraph();
});
});
if (timePills.length > 0) {
timePills[0].classList.add("active");
}
const addBandwidthData = (bytesIn, bytesOut) => {
bandwidthHistory.timestamps.push(Date.now());
bandwidthHistory.bytesIn.push(bytesIn);
bandwidthHistory.bytesOut.push(bytesOut);
if (bandwidthHistory.timestamps.length > 360) {
bandwidthHistory.timestamps.shift();
bandwidthHistory.bytesIn.shift();
bandwidthHistory.bytesOut.shift();
}
};
const getFilteredData = () => {
if (selectedTimeRange === "all") return bandwidthHistory;
const cutoff = Date.now() - selectedTimeRange * 1000;
const startIndex = bandwidthHistory.timestamps.findIndex((t) => t >= cutoff);
if (startIndex === -1) return bandwidthHistory;
return {
timestamps: bandwidthHistory.timestamps.slice(startIndex),
bytesIn: bandwidthHistory.bytesIn.slice(startIndex),
bytesOut: bandwidthHistory.bytesOut.slice(startIndex),
};
};
const drawBandwidthGraph = () => {
if (!bandwidthCanvas || !bandwidthCtx) return;
const w = bandwidthCanvas.width;
const h = bandwidthCanvas.height;
if (w === 0 || h === 0) return;
const pad = { t: 10, r: 10, b: 20, l: 50 };
bandwidthCtx.clearRect(0, 0, w, h);
const data = getFilteredData();
if (data.timestamps.length < 2) return;
const max = Math.max(...data.bytesIn, ...data.bytesOut);
if (max === 0) return;
bandwidthCtx.fillStyle = "#9ca3af";
bandwidthCtx.font =
'10px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto';
bandwidthCtx.textAlign = "right";
[max, max / 2, 0].forEach((val, i) => {
bandwidthCtx.fillText(
formatBandwidth(val, true),
pad.l - 5,
pad.t + ((h - pad.t - pad.b) / 2) * i + 4
);
});
const drawLine = (points, color) => {
bandwidthCtx.strokeStyle = color;
bandwidthCtx.lineWidth = 2;
bandwidthCtx.beginPath();
points.forEach((val, i) => {
const x = pad.l + (i / (points.length - 1)) * (w - pad.l - pad.r);
const y = pad.t + (h - pad.t - pad.b) - (val / max) * (h - pad.t - pad.b);
i === 0 ? bandwidthCtx.moveTo(x, y) : bandwidthCtx.lineTo(x, y);
});
bandwidthCtx.stroke();
bandwidthCtx.lineTo(
pad.l + (w - pad.l - pad.r),
pad.t + (h - pad.t - pad.b)
);
bandwidthCtx.lineTo(pad.l, pad.t + (h - pad.t - pad.b));
bandwidthCtx.closePath();
bandwidthCtx.fillStyle = color + "33";
bandwidthCtx.fill();
};
drawLine(data.bytesIn, "#60a5fa");
drawLine(data.bytesOut, "#f97316");
};
const themes = [
"hypermind.css",
"hypermind-classic.css",
@@ -843,11 +997,11 @@ function showThemeNotification(themeName) {
notification.classList.remove("hidden");
notification.offsetHeight;
notification.classList.add("show");
if (notificationTimeout) clearTimeout(notificationTimeout);
notificationTimeout = setTimeout(() => {
notification.classList.remove("show");
setTimeout(() => {
+31 -8
View File
@@ -128,18 +128,41 @@
<span class="stat-label">Invalid Signatures</span>
<span class="stat-value" id="diag-invalid-sig">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Bandwidth In</span>
<span class="stat-value" id="diag-bandwidth-in">0 KB</span>
</div>
<div class="stat-row">
<span class="stat-label">Bandwidth Out</span>
<span class="stat-value" id="diag-bandwidth-out">0 KB</span>
</div>
<div class="stat-row">
<span class="stat-label">LEAVE Messages</span>
<span class="stat-value" id="diag-leave">0</span>
</div>
<div id="bandwidthOverlay" class="bandwidth-overlay">
<div class="bandwidth-title">Bandwidth</div>
<div class="bandwidth-graph-container">
<canvas id="bandwidthGraph"></canvas>
</div>
<div class="bandwidth-footer">
<div class="bandwidth-legend">
<div class="legend-item">
<span class="legend-color" style="background: #60a5fa"></span>
<span class="stat-value"
>In: <span id="current-in">0 KB</span></span
>
</div>
<div class="legend-item">
<span class="legend-color" style="background: #f97316"></span>
<span class="stat-value"
>Out: <span id="current-out">0 KB</span></span
>
</div>
</div>
<div class="time-pills">
<button class="time-pill" data-value="300">5m</button>
<button class="time-pill" data-value="1800">30m</button>
<button class="time-pill" data-value="all">All</button>
<button class="close-btn" onclick="toggleBandwidthGraph()">
</button>
</div>
</div>
</div>
<div class="update-time" id="last-update">last 10 seconds</div>
</div>
</div>
+56
View File
@@ -156,6 +156,62 @@ a { color: var(--color-text-anchor-link); text-decoration: none; border-bottom:
margin-top: 1rem;
}
.bandwidth-overlay {
padding-top: .5rem;
}
.bandwidth-title {
font-size: 0.85rem;
color: var(--color-modal-stat-label);
margin-bottom: 0.5rem;
letter-spacing: 0.5px;
}
.bandwidth-graph-container {
padding: 0.5rem 0 0;
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
max-height: 150px;
opacity: 1;
overflow: hidden;
}
.bandwidth-overlay.collapsed .bandwidth-graph-container { max-height: 0; opacity: 0; padding: 0; }
.bandwidth-overlay .close-btn {
position: static;
font-size: 0.65rem;
font-weight: bold;
padding: 0.2rem 0.5rem;
color: #9ca3af;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 3px;
cursor: pointer;
transition: all 0.2s;
outline: none;
}
.bandwidth-overlay .close-btn:hover { color: #cbd5e1; border-color: #4b5563; }
#bandwidthGraph { width: 100%; height: 100px; display: block; background: transparent; }
.bandwidth-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0 0.75rem;
}
.bandwidth-legend { display: flex; gap: 1.5rem; }
.legend-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.7rem; }
.legend-color { width: 10px; height: 10px; border-radius: 2px; }
.time-pills { display: flex; gap: 0.3rem; }
.bandwidth-overlay.collapsed .time-pill { display: none; }
.time-pill {
background: transparent;
color: #4b5563;
border: 1px solid #333;
padding: 0.2rem 0.5rem;
font-size: 0.65rem;
border-radius: 3px;
cursor: pointer;
transition: all 0.2s;
outline: none;
}
.time-pill:hover { color: #9ca3af; border-color: #4b5563; }
.time-pill.active { background: #1a1a1a; color: #4ade80; border-color: #4ade80; }
.theme-btn {
position: fixed;
bottom: 1.5rem;