11// Copyright 2026, Command Line Inc.
22// SPDX-License-Identifier: Apache-2.0
33
4+ import { Tooltip } from "@/app/element/tooltip" ;
45import { globalStore } from "@/app/store/jotaiStore" ;
56import { TabRpcClient } from "@/app/store/wshrpcutil" ;
67import { MetaKeyAtomFnType , WaveEnv , WaveEnvSubset } from "@/app/waveenv/waveenv" ;
@@ -212,26 +213,25 @@ type ColDef = {
212213 tooltip ?: string ;
213214 width : string ;
214215 align ?: "right" ;
215- hideOnWindows ?: boolean ;
216+ hideOnPlatform ?: string [ ] ;
216217} ;
217218
218219const Columns : ColDef [ ] = [
219220 { key : "pid" , label : "PID" , width : "70px" , align : "right" } ,
220221 { key : "command" , label : "Command" , width : "minmax(120px, 4fr)" } ,
221- { key : "status" , label : "Status" , width : "75px" , hideOnWindows : true } ,
222+ { key : "status" , label : "Status" , width : "75px" , hideOnPlatform : [ "windows" , "darwin" ] } ,
222223 { key : "user" , label : "User" , width : "80px" } ,
223- { key : "threads" , label : "NT" , tooltip : "Num Threads" , width : "55px" , align : "right" , hideOnWindows : true } ,
224+ { key : "threads" , label : "NT" , tooltip : "Num Threads" , width : "55px" , align : "right" , hideOnPlatform : [ "windows" ] } ,
224225 { key : "cpu" , label : "CPU%" , width : "70px" , align : "right" } ,
225226 { key : "mem" , label : "Memory" , width : "90px" , align : "right" } ,
226227] ;
227228
228- function getColumns ( isWindows : boolean ) : ColDef [ ] {
229- if ( ! isWindows ) return Columns ;
230- return Columns . filter ( ( c ) => ! c . hideOnWindows ) ;
229+ function getColumns ( platform : string ) : ColDef [ ] {
230+ return Columns . filter ( ( c ) => ! c . hideOnPlatform ?. includes ( platform ) ) ;
231231}
232232
233- function getGridTemplate ( isWindows : boolean ) : string {
234- return getColumns ( isWindows )
233+ function getGridTemplate ( platform : string ) : string {
234+ return getColumns ( platform )
235235 . map ( ( c ) => c . width )
236236 . join ( " " ) ;
237237}
@@ -248,30 +248,31 @@ const TableHeader = React.memo(function TableHeader({
248248 model,
249249 sortBy,
250250 sortDesc,
251- isWindows ,
251+ platform ,
252252} : {
253253 model : ProcessViewerViewModel ;
254254 sortBy : SortCol ;
255255 sortDesc : boolean ;
256- isWindows : boolean ;
256+ platform : string ;
257257} ) {
258- const cols = getColumns ( isWindows ) ;
259- const gridTemplate = getGridTemplate ( isWindows ) ;
258+ const cols = getColumns ( platform ) ;
259+ const gridTemplate = getGridTemplate ( platform ) ;
260260 return (
261261 < div
262262 className = "grid w-full shrink-0 border-b border-white/10 bg-panel text-xs text-secondary font-medium select-none"
263263 style = { { gridTemplateColumns : gridTemplate } }
264264 >
265265 { cols . map ( ( col ) => (
266- < div
266+ < Tooltip
267267 key = { col . key }
268- title = { col . tooltip }
269- className = { `px-2 py-1 cursor-pointer hover:text-primary hover:bg-white/5 transition-colors truncate flex items-center${ col . align === "right" ? " justify-end" : "" } ` }
270- onClick = { ( ) => model . setSort ( col . key ) }
268+ content = { col . tooltip }
269+ disable = { ! col . tooltip }
270+ divClassName = { `px-2 py-1 cursor-pointer hover:text-primary hover:bg-white/5 transition-colors truncate flex items-center${ col . align === "right" ? " justify-end" : "" } ` }
271+ divOnClick = { ( ) => model . setSort ( col . key ) }
271272 >
272273 < span className = "truncate" > { col . label } </ span >
273274 < SortIndicator active = { sortBy === col . key } desc = { sortDesc } />
274- </ div >
275+ </ Tooltip >
275276 ) ) }
276277 </ div >
277278 ) ;
@@ -281,23 +282,25 @@ TableHeader.displayName = "TableHeader";
281282const ProcessRow = React . memo ( function ProcessRow ( {
282283 proc,
283284 hasCpu,
284- isWindows ,
285+ platform ,
285286} : {
286287 proc : ProcessInfo ;
287288 hasCpu : boolean ;
288- isWindows : boolean ;
289+ platform : string ;
289290} ) {
290- const gridTemplate = getGridTemplate ( isWindows ) ;
291+ const gridTemplate = getGridTemplate ( platform ) ;
292+ const showStatus = platform !== "windows" && platform !== "darwin" ;
293+ const showThreads = platform !== "windows" ;
291294 return (
292295 < div
293296 className = "grid w-full text-xs hover:bg-white/5 transition-colors"
294297 style = { { gridTemplateColumns : gridTemplate , height : RowHeight } }
295298 >
296299 < div className = "px-2 py-[3px] truncate text-right text-secondary font-mono text-[11px]" > { proc . pid } </ div >
297300 < div className = "px-2 py-[3px] truncate" > { proc . command } </ div >
298- { ! isWindows && < div className = "px-2 py-[3px] truncate text-secondary text-[11px]" > { proc . status } </ div > }
301+ { showStatus && < div className = "px-2 py-[3px] truncate text-secondary text-[11px]" > { proc . status } </ div > }
299302 < div className = "px-2 py-[3px] truncate text-secondary" > { proc . user } </ div >
300- { ! isWindows && (
303+ { showThreads && (
301304 < div className = "px-2 py-[3px] truncate text-right text-secondary font-mono text-[11px]" >
302305 { proc . numthreads > 1 ? proc . numthreads : "" }
303306 </ div >
@@ -326,7 +329,7 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
326329 const filteredCount = data ?. filteredcount ?? 0 ;
327330 const processes = data ?. processes ?? [ ] ;
328331 const hasCpu = data ?. hascpu ?? false ;
329- const isWindows = data ?. iswindows ?? false ;
332+ const platform = data ?. platform ?? "" ;
330333 const startIdx = Math . max ( 0 , Math . floor ( scrollTop / RowHeight ) - OverscanRows ) ;
331334
332335 // track container height
@@ -355,6 +358,7 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
355358 const summary = data ?. summary ;
356359 const memUsedGb = summary ?. memused != null ? ( summary . memused / 1024 / 1024 / 1024 ) . toFixed ( 1 ) : null ;
357360 const memTotalGb = summary ?. memtotal != null ? ( summary . memtotal / 1024 / 1024 / 1024 ) . toFixed ( 1 ) : null ;
361+ const cpuPct = summary ?. cpusum != null && summary ?. numcpu != null && summary . numcpu > 0 ? ( summary . cpusum / summary . numcpu ) . toFixed ( 1 ) : null ;
358362
359363 return (
360364 < div className = "flex flex-col w-full h-full overflow-hidden" ref = { containerRef } >
@@ -373,6 +377,11 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
373377 Mem: { memUsedGb } G / { memTotalGb } G
374378 </ span >
375379 ) }
380+ { cpuPct != null && (
381+ < span >
382+ CPU: { cpuPct } % ({ summary . numcpu } cores)
383+ </ span >
384+ ) }
376385 </ >
377386 ) }
378387 < span className = "ml-auto" >
@@ -392,14 +401,14 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
392401 { error != null && < div className = "px-3 py-2 text-xs text-error shrink-0" > { error } </ div > }
393402
394403 { /* header */ }
395- < TableHeader model = { model } sortBy = { sortBy } sortDesc = { sortDesc } isWindows = { isWindows } />
404+ < TableHeader model = { model } sortBy = { sortBy } sortDesc = { sortDesc } platform = { platform } />
396405
397406 { /* virtualized rows */ }
398407 < div ref = { scrollRef } className = "flex-1 overflow-y-auto overflow-x-auto" onScroll = { handleScroll } >
399408 < div style = { { height : totalHeight , position : "relative" , minWidth : "100%" } } >
400409 < div style = { { position : "absolute" , top : paddingTop , left : 0 , right : 0 , minWidth : "100%" } } >
401410 { processes . map ( ( proc ) => (
402- < ProcessRow key = { proc . pid } proc = { proc } hasCpu = { hasCpu } isWindows = { isWindows } />
411+ < ProcessRow key = { proc . pid } proc = { proc } hasCpu = { hasCpu } platform = { platform } />
403412 ) ) }
404413 </ div >
405414 </ div >
0 commit comments