@@ -90,7 +90,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
9090 const resourceChipRef = useRef < HTMLButtonElement > ( null ) ;
9191 const resourcePopoverRef = useRef < HTMLDivElement > ( null ) ;
9292 const [ expandedSections , setExpandedSections ] = useState < Set < string > > ( new Set ( [ 'pane-app' ] ) ) ;
93- const { snapshot, startActive, stopActive, refresh } = useResourceMonitor ( ) ;
93+ const { snapshot, isLoading : resourceLoading , startActive, stopActive, refresh } = useResourceMonitor ( ) ;
9494 const [ popoverStyle , setPopoverStyle ] = useState < React . CSSProperties > ( { } ) ;
9595
9696 const getPanelActivityStatus = usePanelStore ( s => s . getPanelActivityStatus ) ;
@@ -150,12 +150,16 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
150150
151151 // Resource monitor handlers
152152 const toggleResourcePopover = useCallback ( ( ) => {
153- setShowResourcePopover ( prev => {
154- if ( ! prev ) startActive ( ) ;
155- else stopActive ( ) ;
156- return ! prev ;
157- } ) ;
158- } , [ startActive , stopActive ] ) ;
153+ if ( showResourcePopover ) {
154+ setShowResourcePopover ( false ) ;
155+ stopActive ( ) ;
156+ return ;
157+ }
158+
159+ setShowResourcePopover ( true ) ;
160+ void refresh ( ) ;
161+ startActive ( ) ;
162+ } , [ showResourcePopover , refresh , startActive , stopActive ] ) ;
159163
160164 // Popover positioning
161165 useEffect ( ( ) => {
@@ -230,7 +234,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
230234 } ) ;
231235 } , [ ] ) ;
232236
233- const handleRefresh = useCallback ( ( ) => { refresh ( ) ; } , [ refresh ] ) ;
237+ const handleRefresh = useCallback ( ( ) => { void refresh ( ) ; } , [ refresh ] ) ;
234238
235239 const electronTotalCpu = useMemo ( ( ) =>
236240 snapshot ?. electronProcesses . reduce ( ( sum , p ) => sum + p . cpuPercent , 0 ) ?? 0
@@ -910,7 +914,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
910914 </ div >
911915
912916 { /* Resource monitor popover */ }
913- { showResourcePopover && snapshot && createPortal (
917+ { showResourcePopover && createPortal (
914918 < div
915919 ref = { resourcePopoverRef }
916920 className = "bg-surface-primary border border-border-subtle/60 rounded-lg shadow-dropdown-elevated backdrop-blur-sm animate-dropdown-enter overflow-hidden w-[320px]"
@@ -924,90 +928,99 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
924928 < button
925929 onClick = { handleRefresh }
926930 className = "p-1 rounded text-text-tertiary hover:text-text-primary hover:bg-surface-hover transition-colors"
931+ disabled = { resourceLoading }
927932 >
928- < RefreshCw className = "w-3.5 h-3.5" />
933+ < RefreshCw className = { cn ( "w-3.5 h-3.5" , resourceLoading && "animate-spin" ) } />
929934 </ button >
930935 </ div >
931936
932- { /* Summary */ }
933- < div className = "flex items-center gap-4 px-3 py-2 border-b border-border-secondary" >
934- < span className = "text-sm text-text-secondary" >
935- CPU < strong className = "text-text-primary" > { snapshot . cpuReady ? `${ snapshot . totalCpuPercent . toFixed ( 1 ) } %` : '—' } </ strong >
936- </ span >
937- < span className = "text-sm text-text-secondary" >
938- Memory < strong className = "text-text-primary" > { formatMemory ( snapshot . totalMemoryMB ) } </ strong >
939- </ span >
940- </ div >
937+ { ! snapshot ? (
938+ < div className = "px-3 py-4 text-sm text-text-secondary" >
939+ { resourceLoading ? 'Loading resource usage...' : 'No resource snapshot yet.' }
940+ </ div >
941+ ) : (
942+ < >
943+ { /* Summary */ }
944+ < div className = "flex items-center gap-4 px-3 py-2 border-b border-border-secondary" >
945+ < span className = "text-sm text-text-secondary" >
946+ CPU < strong className = "text-text-primary" > { snapshot . cpuReady ? `${ snapshot . totalCpuPercent . toFixed ( 1 ) } %` : '—' } </ strong >
947+ </ span >
948+ < span className = "text-sm text-text-secondary" >
949+ Memory < strong className = "text-text-primary" > { formatMemory ( snapshot . totalMemoryMB ) } </ strong >
950+ </ span >
951+ </ div >
941952
942- { /* Scrollable content */ }
943- < div className = "max-h-[400px] overflow-y-auto" >
944- { /* Pane App section */ }
945- < div className = "border-b border-border-secondary" >
946- < button
947- onClick = { ( ) => toggleSection ( 'pane-app' ) }
948- className = "flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
949- >
950- < div className = "flex items-center gap-1.5" >
951- { expandedSections . has ( 'pane-app' )
952- ? < ChevronDown className = "w-3 h-3 text-text-quaternary" />
953- : < ChevronRight className = "w-3 h-3 text-text-quaternary" /> }
954- < span className = "text-sm font-medium text-text-primary" > Pane App</ span >
955- </ div >
956- < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono" >
957- < span > { snapshot . cpuReady ? `${ electronTotalCpu . toFixed ( 1 ) } %` : '—' } </ span >
958- < span > { formatMemory ( electronTotalMem ) } </ span >
959- </ div >
960- </ button >
961- { expandedSections . has ( 'pane-app' ) && snapshot . electronProcesses . map ( p => (
962- < div key = { p . pid } className = "flex items-center justify-between px-3 py-1 pl-8" >
963- < span className = "text-xs text-text-secondary" > { p . label } </ span >
964- < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono" >
965- < span > { snapshot . cpuReady ? `${ p . cpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
966- < span > { formatMemory ( p . memoryMB ) } </ span >
967- </ div >
953+ { /* Scrollable content */ }
954+ < div className = "max-h-[400px] overflow-y-auto" >
955+ { /* Pane App section */ }
956+ < div className = "border-b border-border-secondary" >
957+ < button
958+ onClick = { ( ) => toggleSection ( 'pane-app' ) }
959+ className = "flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
960+ >
961+ < div className = "flex items-center gap-1.5" >
962+ { expandedSections . has ( 'pane-app' )
963+ ? < ChevronDown className = "w-3 h-3 text-text-quaternary" />
964+ : < ChevronRight className = "w-3 h-3 text-text-quaternary" /> }
965+ < span className = "text-sm font-medium text-text-primary" > Pane App</ span >
966+ </ div >
967+ < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono" >
968+ < span > { snapshot . cpuReady ? `${ electronTotalCpu . toFixed ( 1 ) } %` : '—' } </ span >
969+ < span > { formatMemory ( electronTotalMem ) } </ span >
970+ </ div >
971+ </ button >
972+ { expandedSections . has ( 'pane-app' ) && snapshot . electronProcesses . map ( p => (
973+ < div key = { p . pid } className = "flex items-center justify-between px-3 py-1 pl-8" >
974+ < span className = "text-xs text-text-secondary" > { p . label } </ span >
975+ < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono" >
976+ < span > { snapshot . cpuReady ? `${ p . cpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
977+ < span > { formatMemory ( p . memoryMB ) } </ span >
978+ </ div >
979+ </ div >
980+ ) ) }
968981 </ div >
969- ) ) }
970- </ div >
971982
972- { /* Per-session sections */ }
973- { snapshot . sessions . map ( sess => (
974- < div
975- key = { sess . sessionId }
976- className = { cn (
977- "border-b border-border-secondary" ,
978- sess . sessionId === session ?. id && "bg-interactive/5"
979- ) }
980- >
981- < button
982- onClick = { ( ) => toggleSection ( sess . sessionId ) }
983- className = "flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
984- >
985- < div className = "flex items-center gap-1.5 min-w-0" >
986- { expandedSections . has ( sess . sessionId )
987- ? < ChevronDown className = "w-3 h-3 text-text-quaternary flex-shrink-0" />
988- : < ChevronRight className = "w-3 h-3 text-text-quaternary flex-shrink-0" /> }
989- { sess . sessionId === session ?. id && (
990- < div className = "w-1.5 h-1.5 rounded-full bg-interactive flex-shrink-0" />
983+ { /* Per-session sections */ }
984+ { snapshot . sessions . map ( sess => (
985+ < div
986+ key = { sess . sessionId }
987+ className = { cn (
988+ "border-b border-border-secondary" ,
989+ sess . sessionId === session ?. id && "bg-interactive/5"
991990 ) }
992- < span className = "text-sm font-medium text-text-primary truncate" > { sess . sessionName } </ span >
993- </ div >
994- < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2" >
995- < span > { snapshot . cpuReady ? `${ sess . totalCpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
996- < span > { formatMemory ( sess . totalMemoryMB ) } </ span >
997- </ div >
998- </ button >
999- { expandedSections . has ( sess . sessionId ) && sess . children . map ( child => (
1000- < div key = { child . pid } className = "flex items-center justify-between px-3 py-1 pl-8" >
1001- < span className = "text-xs text-text-secondary truncate" > { child . name } </ span >
1002- < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2" >
1003- < span > { snapshot . cpuReady ? `${ child . cpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
1004- < span > { formatMemory ( child . memoryMB ) } </ span >
1005- </ div >
991+ >
992+ < button
993+ onClick = { ( ) => toggleSection ( sess . sessionId ) }
994+ className = "flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
995+ >
996+ < div className = "flex items-center gap-1.5 min-w-0" >
997+ { expandedSections . has ( sess . sessionId )
998+ ? < ChevronDown className = "w-3 h-3 text-text-quaternary flex-shrink-0" />
999+ : < ChevronRight className = "w-3 h-3 text-text-quaternary flex-shrink-0" /> }
1000+ { sess . sessionId === session ?. id && (
1001+ < div className = "w-1.5 h-1.5 rounded-full bg-interactive flex-shrink-0" />
1002+ ) }
1003+ < span className = "text-sm font-medium text-text-primary truncate" > { sess . sessionName } </ span >
1004+ </ div >
1005+ < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2" >
1006+ < span > { snapshot . cpuReady ? `${ sess . totalCpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
1007+ < span > { formatMemory ( sess . totalMemoryMB ) } </ span >
1008+ </ div >
1009+ </ button >
1010+ { expandedSections . has ( sess . sessionId ) && sess . children . map ( child => (
1011+ < div key = { child . pid } className = "flex items-center justify-between px-3 py-1 pl-8" >
1012+ < span className = "text-xs text-text-secondary truncate" > { child . name } </ span >
1013+ < div className = "flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2" >
1014+ < span > { snapshot . cpuReady ? `${ child . cpuPercent . toFixed ( 1 ) } %` : '—' } </ span >
1015+ < span > { formatMemory ( child . memoryMB ) } </ span >
1016+ </ div >
1017+ </ div >
1018+ ) ) }
10061019 </ div >
10071020 ) ) }
10081021 </ div >
1009- ) ) }
1010- </ div >
1022+ </ >
1023+ ) }
10111024 </ div > ,
10121025 document . body
10131026 ) }
0 commit comments