@@ -132,6 +132,9 @@ function setupScrollDetection() {
132132// Initialize scroll detection
133133setupScrollDetection ( ) ;
134134
135+ // Track whether the aggregate summary card has been injected
136+ let aggregateCardInjected = false ;
137+
135138// Performance: Batched rendering system using requestAnimationFrame
136139// Batches all DOM updates into a single frame to minimize reflows/repaints
137140let pendingUpdates = new Map ( ) ; // Queue of pending GPU/system updates
@@ -155,6 +158,7 @@ function handleSocketMessage(event) {
155158 // Clear loading state
156159 if ( overviewContainer . querySelector ( '.loading' ) ) {
157160 overviewContainer . innerHTML = '' ;
161+ aggregateCardInjected = false ;
158162 }
159163
160164 const gpuCount = Object . keys ( data . gpus ) . length ;
@@ -241,6 +245,21 @@ function handleSocketMessage(event) {
241245 }
242246 } ) ;
243247
248+ // Aggregate summary card (2+ GPUs)
249+ if ( gpuCount >= 2 ) {
250+ if ( ! aggregateCardInjected ) {
251+ overviewContainer . insertAdjacentHTML ( 'afterbegin' , createAggregateCard ( ) ) ;
252+ aggregateCardInjected = true ;
253+ initAggregateChart ( ) ;
254+ }
255+ pendingUpdates . set ( '_aggregate' , { gpus : data . gpus } ) ;
256+ } else if ( aggregateCardInjected ) {
257+ const aggCard = document . getElementById ( 'aggregate-card' ) ;
258+ if ( aggCard ) aggCard . remove ( ) ;
259+ destroyAggregateChart ( ) ;
260+ aggregateCardInjected = false ;
261+ }
262+
244263 // Queue system updates (processes/CPU/RAM) for batching
245264 if ( ! lastDOMUpdate . system || ( now - lastDOMUpdate . system ) >= DOM_UPDATE_INTERVAL ) {
246265 pendingUpdates . set ( '_system' , {
@@ -273,7 +292,10 @@ function processBatchedUpdates() {
273292
274293 // Execute all queued updates in a single batch
275294 pendingUpdates . forEach ( ( update , gpuId ) => {
276- if ( gpuId === '_system' ) {
295+ if ( gpuId === '_aggregate' ) {
296+ updateAggregateStats ( update . gpus ) ;
297+ return ;
298+ } else if ( gpuId === '_system' ) {
277299 // System updates (CPU, RAM, processes)
278300 updateProcesses ( update . processes ) ;
279301 updateSystemInfo ( update . system ) ;
@@ -416,6 +438,7 @@ function handleClusterData(data) {
416438 // Clear loading state
417439 if ( overviewContainer . querySelector ( '.loading' ) ) {
418440 overviewContainer . innerHTML = '' ;
441+ aggregateCardInjected = false ;
419442 }
420443
421444 // Skip DOM updates during scrolling
@@ -439,7 +462,6 @@ function handleClusterData(data) {
439462 } ) ;
440463 }
441464 updateAllChartDataOnly ( fullGpuId , gpuInfo ) ;
442- // Also update system chart data during scroll (per-node)
443465 if ( nodeData . system ) {
444466 updateGPUSystemCharts ( fullGpuId , nodeData . system , nodeName , false ) ;
445467 }
@@ -525,6 +547,30 @@ function handleClusterData(data) {
525547 }
526548 } ) ;
527549
550+ // Aggregate summary card (2+ GPUs across cluster)
551+ const clusterGpusFlat = { } ;
552+ Object . entries ( data . nodes ) . forEach ( ( [ nodeName , nodeData ] ) => {
553+ if ( nodeData . status === 'online' ) {
554+ Object . entries ( nodeData . gpus ) . forEach ( ( [ gpuId , gpuInfo ] ) => {
555+ clusterGpusFlat [ `${ nodeName } -${ gpuId } ` ] = gpuInfo ;
556+ } ) ;
557+ }
558+ } ) ;
559+ const clusterGpuCount = Object . keys ( clusterGpusFlat ) . length ;
560+ if ( clusterGpuCount >= 2 ) {
561+ if ( ! aggregateCardInjected ) {
562+ overviewContainer . insertAdjacentHTML ( 'afterbegin' , createAggregateCard ( ) ) ;
563+ aggregateCardInjected = true ;
564+ initAggregateChart ( ) ;
565+ }
566+ pendingUpdates . set ( '_aggregate' , { gpus : clusterGpusFlat } ) ;
567+ } else if ( aggregateCardInjected ) {
568+ const aggCard = document . getElementById ( 'aggregate-card' ) ;
569+ if ( aggCard ) aggCard . remove ( ) ;
570+ destroyAggregateChart ( ) ;
571+ aggregateCardInjected = false ;
572+ }
573+
528574 // Update processes and system info (use first online node)
529575 const firstOnlineNode = Object . values ( data . nodes ) . find ( n => n . status === 'online' ) ;
530576 if ( firstOnlineNode ) {
0 commit comments