@@ -14,6 +14,11 @@ import { getPalette, type DcpPalette } from "../shared/theme"
1414import type { DcpRouteNames , DcpTuiClient , DcpTuiConfig } from "../shared/types"
1515
1616const BAR_WIDTH = 12
17+ // Content width derived from graph row: label(9) + space(1) + percent(4) + " |"(2) + bar(12) + "| "(2) + tokens(~5)
18+ const CONTENT_WIDTH = 9 + 1 + 4 + 2 + BAR_WIDTH + 2 + 5
19+
20+ const truncate = ( text : string , max : number ) =>
21+ text . length > max ? text . slice ( 0 , max - 3 ) + "..." : text
1722const REFRESH_DEBOUNCE_MS = 100
1823
1924const toneColor = (
@@ -40,17 +45,16 @@ const SummaryRow = (props: {
4045 label : string
4146 value : string
4247 tone ?: "text" | "muted" | "accent" | "success" | "warning"
48+ marginTop ?: number
4349} ) => {
4450 return (
4551 < box
4652 width = "100%"
47- backgroundColor = { props . palette . surface }
48- paddingLeft = { 1 }
49- paddingRight = { 1 }
5053 flexDirection = "row"
5154 justifyContent = "space-between"
55+ marginTop = { props . marginTop }
5256 >
53- < text fg = { props . palette . muted } > { props . label } </ text >
57+ < text fg = { props . palette . text } > { props . label } </ text >
5458 < text fg = { toneColor ( props . palette , props . tone ) } >
5559 < b > { props . value } </ b >
5660 </ text >
@@ -69,12 +73,15 @@ const SidebarContextBar = (props: {
6973 const percent = createMemo ( ( ) =>
7074 props . total > 0 ? `${ Math . round ( ( props . value / props . total ) * 100 ) } %` : "0%" ,
7175 )
72- const label = createMemo ( ( ) => props . label . padEnd ( 8 , " " ) )
76+ const label = createMemo ( ( ) => props . label . padEnd ( 9 , " " ) )
7377 const bar = createMemo ( ( ) => buildBar ( props . value , props . total , props . char ) )
7478 return (
75- < text
76- fg = { toneColor ( props . palette , props . tone ) }
77- > { `${ label ( ) } ${ percent ( ) . padStart ( 4 , " " ) } |${ bar ( ) } | ${ compactTokenCount ( props . value ) } ` } </ text >
79+ < box flexDirection = "row" >
80+ < text fg = { props . palette . text } > { label ( ) } </ text >
81+ < text fg = { toneColor ( props . palette , props . tone ) } >
82+ { ` ${ percent ( ) . padStart ( 4 , " " ) } |${ bar ( ) } | ${ compactTokenCount ( props . value ) . padStart ( 5 , " " ) } ` }
83+ </ text >
84+ </ box >
7885 )
7986}
8087
@@ -149,6 +156,7 @@ const SidebarContext = (props: {
149156 }
150157
151158 const cached = peekContextSnapshot ( sessionID )
159+ let silentRefresh = false
152160 if ( cached ) {
153161 void props . logger . debug ( "Sidebar using cached snapshot before reload" , {
154162 sessionID,
@@ -159,18 +167,24 @@ const SidebarContext = (props: {
159167 setLoading ( false )
160168 } else {
161169 const current = untrack ( snapshot )
162- if ( ! preserveSnapshot || current ?. sessionID !== sessionID ) {
170+ if ( preserveSnapshot && current ?. sessionID === sessionID ) {
171+ silentRefresh = true
172+ void props . logger . debug ( "Sidebar silent refresh, keeping current snapshot" , {
173+ sessionID,
174+ } )
175+ } else {
163176 setSnapshot ( createPlaceholderContextSnapshot ( sessionID , [ "Loading DCP context..." ] ) )
177+ setLoading ( true )
178+ void props . logger . debug ( "Sidebar entering loading state" , {
179+ sessionID,
180+ hadCurrentSnapshot : ! ! current ,
181+ } )
164182 }
165- setLoading ( true )
166- void props . logger . debug ( "Sidebar entering loading state" , {
167- sessionID,
168- hadCurrentSnapshot : ! ! current ,
169- preservedSnapshot : preserveSnapshot && current ?. sessionID === sessionID ,
170- } )
171183 }
172184 setError ( undefined )
173- requestRender ( "refresh-start" , { sessionID, reason } )
185+ if ( ! silentRefresh ) {
186+ requestRender ( "refresh-start" , { sessionID, reason } )
187+ }
174188
175189 const currentRequest = ++ requestVersion
176190 void props . logger . debug ( "Sidebar refresh request issued" , {
@@ -357,44 +371,14 @@ const SidebarContext = (props: {
357371 ) ,
358372 )
359373
360- const prunedItems = createMemo ( ( ) => {
361- const value = snapshot ( )
362- const parts : string [ ] = [ ]
363- if ( value . breakdown . prunedToolCount > 0 ) {
364- parts . push (
365- `${ value . breakdown . prunedToolCount } tool${ value . breakdown . prunedToolCount === 1 ? "" : "s" } ` ,
366- )
367- }
368- if ( value . breakdown . prunedMessageCount > 0 ) {
369- parts . push (
370- `${ value . breakdown . prunedMessageCount } msg${ value . breakdown . prunedMessageCount === 1 ? "" : "s" } ` ,
371- )
372- }
373- return parts . length > 0 ? `${ parts . join ( ", " ) } pruned` : "No pruned items"
374- } )
375-
376374 const blockSummary = createMemo ( ( ) => {
377375 return `${ snapshot ( ) . persisted . activeBlockCount } `
378376 } )
379377
380- const topicLine = createMemo ( ( ) => {
381- const value = snapshot ( )
382- if ( ! value . persisted . activeBlockTopics . length ) return ""
383- return `Topics: ${ value . persisted . activeBlockTopics . join ( " | " ) } `
384- } )
385-
386- const noteLine = createMemo ( ( ) => {
387- const topic = topicLine ( )
388- if ( topic ) return topic
389- return snapshot ( ) . notes [ 0 ] ?? ""
390- } )
391-
392- const stateLine = createMemo ( ( ) => {
393- if ( error ( ) && snapshot ( ) . breakdown . total === 0 ) return "DCP context failed to load."
394- if ( error ( ) ) return `Refresh failed: ${ error ( ) } `
395- if ( loading ( ) ) return "Loading DCP context..."
396- return "DCP context loaded."
397- } )
378+ const topics = createMemo ( ( ) => snapshot ( ) . persisted . activeBlockTopics )
379+ const topicTotal = createMemo ( ( ) => snapshot ( ) . persisted . activeBlockTopicTotal )
380+ const topicOverflow = createMemo ( ( ) => topicTotal ( ) - topics ( ) . length )
381+ const fallbackNote = createMemo ( ( ) => snapshot ( ) . notes [ 0 ] ?? "" )
398382
399383 const status = createMemo ( ( ) => {
400384 if ( error ( ) && snapshot ( ) . breakdown . total > 0 )
@@ -411,7 +395,7 @@ const SidebarContext = (props: {
411395 width = "100%"
412396 flexDirection = "column"
413397 gap = { 0 }
414- backgroundColor = { props . palette . base }
398+ backgroundColor = { props . palette . surface }
415399 border = { { type : "single" } }
416400 borderColor = { props . palette . accent }
417401 paddingTop = { 1 }
@@ -427,87 +411,86 @@ const SidebarContext = (props: {
427411 < b > { props . config . label } </ b >
428412 </ text >
429413 </ box >
430- < text fg = { props . palette . muted } > click for more</ text >
414+ < text fg = { props . palette . text } > click for more</ text >
431415 </ box >
432416 < text fg = { toneColor ( props . palette , status ( ) . tone ) } > { status ( ) . label } </ text >
433417 </ box >
434418
435419 < box flexDirection = "row" justifyContent = "space-between" >
436- < text fg = { props . palette . muted } > session { props . sessionID ( ) . slice ( 0 , 18 ) } </ text >
437- </ box >
438-
439- < box paddingTop = { 1 } >
440- < text fg = { error ( ) ? props . palette . warning : props . palette . muted } >
441- { stateLine ( ) }
420+ < text fg = { props . palette . muted } >
421+ { props . sessionID ( ) . length > 27
422+ ? props . sessionID ( ) . slice ( 0 , 27 ) + "..."
423+ : props . sessionID ( ) }
442424 </ text >
443425 </ box >
444426
427+ < SummaryRow
428+ palette = { props . palette }
429+ label = "Saved"
430+ value = { `~${ compactTokenCount ( snapshot ( ) . breakdown . prunedTokens ) } ` }
431+ tone = "accent"
432+ marginTop = { 1 }
433+ />
434+ < SummaryRow
435+ palette = { props . palette }
436+ label = "Compressions"
437+ value = { blockSummary ( ) }
438+ tone = "accent"
439+ />
440+
445441 < box width = "100%" flexDirection = "column" gap = { 0 } paddingTop = { 1 } >
446- < box
447- width = "100%"
448- flexDirection = "row"
449- justifyContent = "space-between"
450- backgroundColor = { props . palette . surface }
451- paddingLeft = { 1 }
452- paddingRight = { 1 }
453- >
454- < text fg = { props . palette . muted } > Current</ text >
455- < text fg = { props . palette . accent } >
456- < b > ~{ compactTokenCount ( snapshot ( ) . breakdown . total ) } </ b >
457- </ text >
458- </ box >
459- < SummaryRow
442+ < SidebarContextBar
460443 palette = { props . palette }
461- label = "Saved"
462- value = { `~${ compactTokenCount ( snapshot ( ) . breakdown . prunedTokens ) } ` }
463- tone = "success"
444+ label = "System"
445+ value = { snapshot ( ) . breakdown . system }
446+ total = { snapshot ( ) . breakdown . total }
447+ char = "█"
448+ tone = "accent"
464449 />
465- < SummaryRow
450+ < SidebarContextBar
466451 palette = { props . palette }
467- label = "Compressions"
468- value = { blockSummary ( ) }
452+ label = "User"
453+ value = { snapshot ( ) . breakdown . user }
454+ total = { snapshot ( ) . breakdown . total }
455+ char = "█"
469456 tone = "accent"
470457 />
458+ < SidebarContextBar
459+ palette = { props . palette }
460+ label = "Assistant"
461+ value = { snapshot ( ) . breakdown . assistant }
462+ total = { snapshot ( ) . breakdown . total }
463+ char = "█"
464+ tone = "accent"
465+ />
466+ < SidebarContextBar
467+ palette = { props . palette }
468+ label = "Tools"
469+ value = { snapshot ( ) . breakdown . tools }
470+ total = { snapshot ( ) . breakdown . total }
471+ char = "█"
472+ tone = "accent"
473+ />
474+ </ box >
471475
472- < box width = "100%" flexDirection = "column" gap = { 0 } paddingTop = { 1 } >
473- < SidebarContextBar
474- palette = { props . palette }
475- label = "System"
476- value = { snapshot ( ) . breakdown . system }
477- total = { snapshot ( ) . breakdown . total }
478- char = "█"
479- tone = "accent"
480- />
481- < SidebarContextBar
482- palette = { props . palette }
483- label = "User"
484- value = { snapshot ( ) . breakdown . user }
485- total = { snapshot ( ) . breakdown . total }
486- char = "▓"
487- tone = "text"
488- />
489- < SidebarContextBar
490- palette = { props . palette }
491- label = "Assist"
492- value = { snapshot ( ) . breakdown . assistant }
493- total = { snapshot ( ) . breakdown . total }
494- char = "▒"
495- tone = "muted"
496- />
497- < SidebarContextBar
498- palette = { props . palette }
499- label = "Tools"
500- value = { snapshot ( ) . breakdown . tools }
501- total = { snapshot ( ) . breakdown . total }
502- char = "░"
503- tone = "warning"
504- />
505- </ box >
506-
507- < box width = "100%" flexDirection = "column" gap = { 0 } paddingTop = { 1 } >
508- < text fg = { props . palette . muted } > { prunedItems ( ) } </ text >
509- < text fg = { props . palette . muted } > { noteLine ( ) } </ text >
510- </ box >
476+ < box width = "100%" flexDirection = "column" gap = { 0 } paddingTop = { 1 } >
477+ { topics ( ) . length > 0 ? (
478+ < >
479+ < text fg = { props . palette . text } >
480+ < b > Compressed Topics</ b >
481+ </ text >
482+ { topics ( ) . map ( ( t ) => (
483+ < text fg = { props . palette . muted } > { truncate ( t , CONTENT_WIDTH ) } </ text >
484+ ) ) }
485+ { topicOverflow ( ) > 0 ? (
486+ < text fg = { props . palette . muted } dim >
487+ ... { topicOverflow ( ) } more topics
488+ </ text >
489+ ) : null }
490+ </ >
491+ ) : fallbackNote ( ) ? (
492+ < text fg = { props . palette . muted } > { fallbackNote ( ) } </ text >
493+ ) : null }
511494 </ box >
512495 </ box >
513496 )
0 commit comments