@@ -183,8 +183,12 @@ export function StoreProvider(props: ParentProps) {
183183 `[Store] StoreProvider: Initializing new store instance at ${ new Date ( ) . toISOString ( ) } ` ,
184184 ) ;
185185 const [ store , setStore ] = createStore < StoreState > ( {
186+ serverSpans : [ ] ,
187+ serverMetrics : [ ] ,
186188 spans : [ ] ,
187189 metrics : [ ] ,
190+ spansByClient : { } ,
191+ metricsByClient : { } ,
188192 clients : [ ] ,
189193 activeClient : Option . none ( ) ,
190194 serverStatus : "starting" ,
@@ -232,51 +236,49 @@ export function StoreProvider(props: ParentProps) {
232236 setStore ( "debugCounter" , ( c ) => c + 1 ) ;
233237 } , 1000 ) ;
234238
235- // Span buffer for batching updates
236- let spanBuffer : SimpleSpan [ ] = [ ] ;
237- let spanUpdateBuffer : Map < string , SimpleSpan > = new Map ( ) ;
238- let eventBuffer : Map < string , SimpleSpanEvent [ ] > = new Map ( ) ; // Buffer events by spanId
239-
240- const flushSpans = ( ) => {
241- if ( spanBuffer . length > 0 || spanUpdateBuffer . size > 0 ) {
242- const newSpans = [ ...spanBuffer ] ;
243- const updates = new Map ( spanUpdateBuffer ) ;
244- spanBuffer = [ ] ;
245- spanUpdateBuffer = new Map ( ) ;
246-
247- // Use direct setStore path-based updates for better reactivity
248- // First, apply updates to existing spans
249- for ( const [ spanId , updatedSpan ] of updates ) {
250- const idx = store . spans . findIndex ( ( s ) => s . spanId === spanId ) ;
251- if ( idx >= 0 ) {
252- setStore ( "spans" , idx , updatedSpan ) ;
253- }
254- }
239+ let eventBuffer : Map < string , SimpleSpanEvent [ ] > = new Map ( ) ;
255240
256- // Add new spans
257- if ( newSpans . length > 0 ) {
258- const currentSpans = [ ...store . spans ] ;
241+ const sourceKey = ( clientId ?: number ) =>
242+ `${ clientId === undefined ? "server" : clientId } ` ;
259243
260- // Apply buffered events to new spans before adding them
261- for ( const span of newSpans ) {
262- const bufferedEvents = eventBuffer . get ( span . spanId ) ;
263- if ( bufferedEvents && bufferedEvents . length > 0 ) {
264- span . events = bufferedEvents ;
265- eventBuffer . delete ( span . spanId ) ;
266- }
267- }
244+ const isVisibleSource = ( clientId ?: number ) =>
245+ store . activeClient . _tag === "Some"
246+ ? clientId === store . activeClient . value . id
247+ : clientId === undefined ;
268248
269- const allSpans = [ ...currentSpans , ...newSpans ] ;
249+ const getSourceSpans = ( clientId ?: number ) =>
250+ clientId === undefined
251+ ? store . serverSpans
252+ : ( store . spansByClient [ clientId ] ?? [ ] ) ;
270253
271- // No automatic pruning - let spans accumulate
272- // Users can press 'c' to clear spans manually when needed
273- setStore ( "spans" , allSpans ) ;
274- }
254+ const setSourceSpans = ( nextSpans : SimpleSpan [ ] , clientId ?: number ) => {
255+ if ( clientId === undefined ) {
256+ setStore ( "serverSpans" , nextSpans ) ;
257+ } else {
258+ setStore ( "spansByClient" , clientId , nextSpans ) ;
259+ }
260+
261+ if ( isVisibleSource ( clientId ) ) {
262+ setStore ( "spans" , nextSpans ) ;
275263 }
276264 } ;
277265
278- // Flush spans periodically
279- setInterval ( flushSpans , 100 ) ;
266+ const getSourceMetrics = ( clientId ?: number ) =>
267+ clientId === undefined
268+ ? store . serverMetrics
269+ : ( store . metricsByClient [ clientId ] ?? [ ] ) ;
270+
271+ const setSourceMetrics = ( nextMetrics : SimpleMetric [ ] , clientId ?: number ) => {
272+ if ( clientId === undefined ) {
273+ setStore ( "serverMetrics" , nextMetrics ) ;
274+ } else {
275+ setStore ( "metricsByClient" , clientId , nextMetrics ) ;
276+ }
277+
278+ if ( isVisibleSource ( clientId ) ) {
279+ setStore ( "metrics" , nextMetrics ) ;
280+ }
281+ } ;
280282
281283 // Helper types for navigation
282284 type NavigableItem = { type : "span" ; span : SimpleSpan } ;
@@ -348,51 +350,78 @@ export function StoreProvider(props: ParentProps) {
348350 } ;
349351
350352 const actions : StoreActions = {
351- addSpan : ( span : Domain . Span ) => {
353+ addSpan : ( span : Domain . Span , clientId ?: number ) => {
352354 const simple = simplifySpan ( span ) ;
353- // Check if span already exists
354- const existing = store . spans . find ( ( s ) => s . spanId === simple . spanId ) ;
355- console . log (
356- `[Store] addSpan: ${ simple . name } , store has ${ store . spans . length } spans, existing=${ ! ! existing } ` ,
357- ) ;
358- if ( existing ) {
359- spanUpdateBuffer . set ( simple . spanId , simple ) ;
355+ const spans = [ ...getSourceSpans ( clientId ) ] ;
356+ const idx = spans . findIndex ( ( s ) => s . spanId === simple . spanId ) ;
357+
358+ const bufferedEvents = eventBuffer . get ( `${ sourceKey ( clientId ) } :${ simple . spanId } ` ) ;
359+ if ( bufferedEvents && bufferedEvents . length > 0 ) {
360+ simple . events = bufferedEvents ;
361+ eventBuffer . delete ( `${ sourceKey ( clientId ) } :${ simple . spanId } ` ) ;
362+ }
363+
364+ if ( idx >= 0 ) {
365+ spans [ idx ] = simple ;
360366 } else {
361- spanBuffer . push ( simple ) ;
367+ spans . push ( simple ) ;
362368 }
369+
370+ setSourceSpans ( spans , clientId ) ;
363371 } ,
364372
365- updateSpan : ( span : Domain . Span ) => {
366- spanUpdateBuffer . set ( span . spanId , simplifySpan ( span ) ) ;
373+ updateSpan : ( span : Domain . Span , clientId ?: number ) => {
374+ const simple = simplifySpan ( span ) ;
375+ const spans = [ ...getSourceSpans ( clientId ) ] ;
376+ const idx = spans . findIndex ( ( s ) => s . spanId === simple . spanId ) ;
377+
378+ if ( idx >= 0 ) {
379+ spans [ idx ] = simple ;
380+ } else {
381+ spans . push ( simple ) ;
382+ }
383+
384+ setSourceSpans ( spans , clientId ) ;
367385 } ,
368386
369- addSpanEvent : ( event : Domain . SpanEvent ) => {
387+ addSpanEvent : ( event : Domain . SpanEvent , clientId ?: number ) => {
370388 const spanId = event . spanId ;
371389 const simpleEvent = simplifySpanEvent ( event ) ;
390+ const spans = [ ...getSourceSpans ( clientId ) ] ;
372391
373- // Try to find the span in the store
374- const idx = store . spans . findIndex ( ( s ) => s . spanId === spanId ) ;
392+ const idx = spans . findIndex ( ( s ) => s . spanId === spanId ) ;
375393
376394 if ( idx >= 0 ) {
377- // Span exists, add event directly
378- console . log (
379- `[Store] addSpanEvent: Adding event " ${ simpleEvent . name } " to span ${ spanId . substring ( 0 , 8 ) } ` ,
380- ) ;
381- setStore ( " spans" , idx , "events" , ( events ) => [ ... events , simpleEvent ] ) ;
395+ spans [ idx ] = {
396+ ... spans [ idx ] ,
397+ events : [ ... spans [ idx ] . events , simpleEvent ] ,
398+ } ;
399+ setSourceSpans ( spans , clientId ) ;
382400 } else {
383- // Span doesn't exist yet, buffer the event
384- console . log (
385- `[Store] addSpanEvent: Buffering event "${ simpleEvent . name } " for span ${ spanId . substring ( 0 , 8 ) } ` ,
386- ) ;
387- const buffered = eventBuffer . get ( spanId ) || [ ] ;
401+ const key = `${ sourceKey ( clientId ) } :${ spanId } ` ;
402+ const buffered = eventBuffer . get ( key ) || [ ] ;
388403 buffered . push ( simpleEvent ) ;
389- eventBuffer . set ( spanId , buffered ) ;
404+ eventBuffer . set ( key , buffered ) ;
390405 }
391406 } ,
392407
393408 clearSpans : ( ) => {
394- spanBuffer = [ ] ;
395- spanUpdateBuffer . clear ( ) ;
409+ const sourcePrefix =
410+ store . activeClient . _tag === "Some"
411+ ? `${ sourceKey ( store . activeClient . value . id ) } :`
412+ : `${ sourceKey ( undefined ) } :` ;
413+ for ( const key of eventBuffer . keys ( ) ) {
414+ if ( key . startsWith ( sourcePrefix ) ) {
415+ eventBuffer . delete ( key ) ;
416+ }
417+ }
418+
419+ if ( store . activeClient . _tag === "Some" ) {
420+ setStore ( "spansByClient" , store . activeClient . value . id , [ ] ) ;
421+ } else {
422+ setStore ( "serverSpans" , [ ] ) ;
423+ }
424+
396425 batch ( ( ) => {
397426 setStore ( "spans" , [ ] ) ;
398427 setStore ( "ui" , "selectedSpanId" , null ) ;
@@ -444,14 +473,18 @@ export function StoreProvider(props: ParentProps) {
444473 ) ;
445474 } ,
446475
447- updateMetrics : ( snapshot : Domain . MetricsSnapshot ) => {
476+ updateMetrics : ( snapshot : Domain . MetricsSnapshot , clientId ?: number ) => {
448477 const metrics = ( snapshot . metrics as Domain . Metric [ ] ) . map ( simplifyMetric ) ;
449- batch ( ( ) => {
450- setStore ( "metrics" , metrics ) ;
451- } ) ;
478+ setSourceMetrics ( metrics , clientId ) ;
452479 } ,
453480
454481 clearMetrics : ( ) => {
482+ if ( store . activeClient . _tag === "Some" ) {
483+ setStore ( "metricsByClient" , store . activeClient . value . id , [ ] ) ;
484+ } else {
485+ setStore ( "serverMetrics" , [ ] ) ;
486+ }
487+
455488 batch ( ( ) => {
456489 setStore ( "metrics" , [ ] ) ;
457490 setStore ( "ui" , "selectedMetricName" , null ) ;
@@ -464,13 +497,45 @@ export function StoreProvider(props: ParentProps) {
464497
465498 setClientsFromHashSet : ( newClients : HashSet . HashSet < Client > ) => {
466499 batch ( ( ) => {
467- setStore ( "clients" , Array . from ( newClients ) ) ;
500+ const nextClients = Array . from ( newClients ) ;
501+ setStore ( "clients" , nextClients ) ;
502+
503+ const nextIds = new Set ( nextClients . map ( ( client ) => client . id ) ) ;
504+
505+ setStore (
506+ "spansByClient" ,
507+ produce ( ( draft ) => {
508+ for ( const clientIdStr of Object . keys ( draft ) ) {
509+ if ( ! nextIds . has ( Number ( clientIdStr ) ) ) {
510+ delete draft [ Number ( clientIdStr ) ] ;
511+ }
512+ }
513+ } ) ,
514+ ) ;
515+
516+ setStore (
517+ "metricsByClient" ,
518+ produce ( ( draft ) => {
519+ for ( const clientIdStr of Object . keys ( draft ) ) {
520+ if ( ! nextIds . has ( Number ( clientIdStr ) ) ) {
521+ delete draft [ Number ( clientIdStr ) ] ;
522+ }
523+ }
524+ } ) ,
525+ ) ;
468526 } ) ;
469527 } ,
470528
471529 setActiveClient : ( client : Option . Option < Client > ) => {
472530 batch ( ( ) => {
473531 setStore ( "activeClient" , client ) ;
532+ if ( client . _tag === "Some" ) {
533+ setStore ( "spans" , store . spansByClient [ client . value . id ] ?? [ ] ) ;
534+ setStore ( "metrics" , store . metricsByClient [ client . value . id ] ?? [ ] ) ;
535+ } else {
536+ setStore ( "spans" , store . serverSpans ) ;
537+ setStore ( "metrics" , store . serverMetrics ) ;
538+ }
474539 } ) ;
475540 } ,
476541
@@ -485,6 +550,8 @@ export function StoreProvider(props: ParentProps) {
485550 if ( client ) {
486551 setStore ( "ui" , "selectedClientIndex" , index ) ;
487552 setStore ( "activeClient" , Option . some ( client ) ) ;
553+ setStore ( "spans" , store . spansByClient [ client . id ] ?? [ ] ) ;
554+ setStore ( "metrics" , store . metricsByClient [ client . id ] ?? [ ] ) ;
488555 console . log (
489556 `[Store] selectClientByIndex: Selected client ${ index } : ${ client . name } ` ,
490557 ) ;
0 commit comments