diff --git a/dashboard/src/app.css b/dashboard/src/app.css
index fc532578..a55c64af 100644
--- a/dashboard/src/app.css
+++ b/dashboard/src/app.css
@@ -198,8 +198,10 @@
stroke: oklch(0.85 0.18 85 / 0.4);
stroke-width: 1.5px;
stroke-dasharray: 8, 8;
- animation: flowAnimation 1s linear infinite;
+ animation: flowAnimation 1.5s linear infinite;
filter: drop-shadow(0 0 3px oklch(0.85 0.18 85 / 0.5));
+ /* GPU optimization - hint to browser this element will animate */
+ will-change: stroke-dashoffset;
}
.graph-link-active {
@@ -208,6 +210,24 @@
filter: drop-shadow(0 0 6px oklch(0.85 0.18 85 / 0.8));
}
+/* Reduce motion for users who prefer it - also saves GPU */
+@media (prefers-reduced-motion: reduce) {
+ .graph-link {
+ animation: none;
+ }
+
+ .shooting-star {
+ animation: none;
+ display: none;
+ }
+
+ .status-pulse,
+ .cursor-blink,
+ .animate-pulse {
+ animation: none;
+ }
+}
+
/* CRT Screen effect for topology */
.crt-screen {
position: relative;
@@ -266,13 +286,15 @@ input:focus, textarea:focus {
box-shadow: none;
}
-/* Shooting Stars Animation */
+/* Shooting Stars Animation - GPU optimized */
.shooting-stars {
position: fixed;
inset: 0;
overflow: hidden;
pointer-events: none;
z-index: 0;
+ /* Only render when visible */
+ content-visibility: auto;
}
.shooting-star {
@@ -285,6 +307,9 @@ input:focus, textarea:focus {
animation: shootingStar var(--duration, 3s) linear infinite;
animation-delay: var(--delay, 0s);
opacity: 0;
+ /* GPU optimization */
+ will-change: transform, opacity;
+ transform: translateZ(0);
}
.shooting-star::before {
@@ -320,3 +345,13 @@ input:focus, textarea:focus {
transform: translate(400px, 400px);
}
}
+
+/* Pause animations when page is hidden to save resources */
+:root:has(body[data-page-hidden="true"]) {
+ .shooting-star,
+ .graph-link,
+ .status-pulse,
+ .cursor-blink {
+ animation-play-state: paused;
+ }
+}
diff --git a/dashboard/src/lib/components/TopologyGraph.svelte b/dashboard/src/lib/components/TopologyGraph.svelte
index 319dc547..99d3f854 100644
--- a/dashboard/src/lib/components/TopologyGraph.svelte
+++ b/dashboard/src/lib/components/TopologyGraph.svelte
@@ -1,5 +1,5 @@
@@ -9,7 +27,7 @@
-
+
{@render children?.()}
diff --git a/dashboard/src/routes/+page.svelte b/dashboard/src/routes/+page.svelte
index 9f75eac8..759ba89a 100644
--- a/dashboard/src/routes/+page.svelte
+++ b/dashboard/src/routes/+page.svelte
@@ -99,17 +99,35 @@ function toggleInstanceDownloadDetails(nodeId: string): void {
}
// Compute highlighted nodes from hovered instance or hovered preview
+ // Memoized to avoid creating new Sets on every render
+ let lastHighlightedNodesKey = '';
+ let cachedHighlightedNodes: Set
= new Set();
+
const highlightedNodes = $derived(() => {
+ // Create a key for the current state to enable memoization
+ const previewKey = Array.from(hoveredPreviewNodes).sort().join(',');
+ const currentKey = `${hoveredInstanceId || 'null'}:${previewKey}`;
+
+ // Return cached value if nothing changed
+ if (currentKey === lastHighlightedNodesKey) {
+ return cachedHighlightedNodes;
+ }
+
+ lastHighlightedNodesKey = currentKey;
+
// First check instance hover
if (hoveredInstanceId) {
const instanceWrapped = instanceData[hoveredInstanceId];
- return unwrapInstanceNodes(instanceWrapped);
+ cachedHighlightedNodes = unwrapInstanceNodes(instanceWrapped);
+ return cachedHighlightedNodes;
}
// Then check preview hover
if (hoveredPreviewNodes.size > 0) {
- return hoveredPreviewNodes;
+ cachedHighlightedNodes = hoveredPreviewNodes;
+ return cachedHighlightedNodes;
}
- return new Set();
+ cachedHighlightedNodes = new Set();
+ return cachedHighlightedNodes;
});
// Helper to estimate memory from model ID (mirrors ModelCard logic)
@@ -516,12 +534,13 @@ function toggleInstanceDownloadDetails(nodeId: string): void {
};
}
- // Debug: Log downloads data when it changes
- $effect(() => {
- if (downloadsData && Object.keys(downloadsData).length > 0) {
- console.log('[Download Debug] Current downloads:', downloadsData);
- }
- });
+ // Debug: Log downloads data when it changes (disabled in production for performance)
+ // Uncomment for debugging:
+ // $effect(() => {
+ // if (downloadsData && Object.keys(downloadsData).length > 0) {
+ // console.log('[Download Debug] Current downloads:', downloadsData);
+ // }
+ // });
// Helper to get download status for an instance
function getInstanceDownloadStatus(instanceId: string, instanceWrapped: unknown): {