mirror of
https://github.com/lklynet/hypermind.git
synced 2026-05-03 17:40:29 +00:00
+163
-9
@@ -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
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user