Skip to content

Commit 3ec3f31

Browse files
committed
formatting
1 parent 8d6c132 commit 3ec3f31

1 file changed

Lines changed: 156 additions & 77 deletions

File tree

frontend/app/view/processviewer/processviewer.tsx

Lines changed: 156 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ function formatNumber4(n: number): string {
3434

3535
function fmtMem(bytes: number): string {
3636
if (bytes == null) return "";
37-
if (bytes < 1024) return formatNumber4(bytes) + " B";
38-
if (bytes < 1024 * 1024) return formatNumber4(bytes / 1024) + " K";
39-
if (bytes < 1024 * 1024 * 1024) return formatNumber4(bytes / 1024 / 1024) + " M";
40-
return formatNumber4(bytes / 1024 / 1024 / 1024) + " G";
37+
if (bytes < 1024) return formatNumber4(bytes) + "B";
38+
if (bytes < 1024 * 1024) return formatNumber4(bytes / 1024) + "K";
39+
if (bytes < 1024 * 1024 * 1024) return formatNumber4(bytes / 1024 / 1024) + "M";
40+
return formatNumber4(bytes / 1024 / 1024 / 1024) + "G";
4141
}
4242

4343
function fmtCpu(cpu: number): string {
@@ -251,7 +251,7 @@ const Columns: ColDef[] = [
251251
{ key: "command", label: "Command", width: "minmax(120px, 4fr)" },
252252
{ key: "status", label: "Status", width: "75px", hideOnPlatform: ["windows", "darwin"] },
253253
{ key: "user", label: "User", width: "80px" },
254-
{ key: "threads", label: "NT", tooltip: "Num Threads", width: "55px", align: "right", hideOnPlatform: ["windows"] },
254+
{ key: "threads", label: "NT", tooltip: "Num Threads", width: "40px", align: "right", hideOnPlatform: ["windows"] },
255255
{ key: "cpu", label: "CPU%", width: "70px", align: "right" },
256256
{ key: "mem", label: "Memory", width: "90px", align: "right" },
257257
];
@@ -403,6 +403,130 @@ const ProcessRow = React.memo(function ProcessRow({
403403
});
404404
ProcessRow.displayName = "ProcessRow";
405405

406+
type StatusBarProps = {
407+
model: ProcessViewerViewModel;
408+
data: ProcessListResponse;
409+
loading: boolean;
410+
error: string;
411+
wide: boolean;
412+
};
413+
414+
const StatusBar = React.memo(function StatusBar({ model, data, loading, error, wide }: StatusBarProps) {
415+
const totalCount = data?.totalcount ?? 0;
416+
const filteredCount = data?.filteredcount ?? 0;
417+
const summary = data?.summary;
418+
const memUsedFmt = summary?.memused != null ? fmtMem(summary.memused) : null;
419+
const memTotalFmt = summary?.memtotal != null ? fmtMem(summary.memtotal) : null;
420+
const cpuPct =
421+
summary?.cpusum != null && summary?.numcpu != null && summary.numcpu > 0
422+
? (summary.cpusum / summary.numcpu).toFixed(1).padStart(6, " ")
423+
: null;
424+
425+
const procCountValue =
426+
totalCount > 0
427+
? filteredCount < totalCount
428+
? `${filteredCount} / ${totalCount}`
429+
: String(totalCount).padStart(5, " ")
430+
: loading
431+
? "…"
432+
: error
433+
? "Err"
434+
: "";
435+
436+
const hasSummaryLoad = summary != null && summary.load1 != null;
437+
const hasSummaryMem = summary != null && memUsedFmt != null;
438+
const hasSummaryCpu = summary != null && cpuPct != null;
439+
440+
if (wide) {
441+
return (
442+
<div className="shrink-0 text-xs text-secondary border-b border-white/10 bg-panel flex items-center gap-2 px-2 py-1">
443+
<div className="shrink-0 flex items-center">
444+
<StatusIndicator model={model} />
445+
</div>
446+
{hasSummaryLoad && (
447+
<span className="shrink-0 whitespace-pre">
448+
Load{" "}
449+
<span className="font-mono text-[11px]">
450+
{fmtLoad(summary.load1)} {fmtLoad(summary.load5)} {fmtLoad(summary.load15)}
451+
</span>
452+
</span>
453+
)}
454+
{hasSummaryMem && (
455+
<>
456+
<div className="w-px self-stretch bg-white/10 shrink-0" />
457+
<span className="shrink-0 whitespace-pre">
458+
Mem{" "}
459+
<span className="font-mono text-[11px]">
460+
{memUsedFmt} / {memTotalFmt}
461+
</span>
462+
</span>
463+
</>
464+
)}
465+
{hasSummaryCpu && (
466+
<>
467+
<div className="w-px self-stretch bg-white/10 shrink-0" />
468+
<Tooltip content={`${summary.numcpu} cores`} placement="bottom">
469+
<span className="shrink-0 cursor-default whitespace-pre">
470+
CPU<span className="font-mono text-[11px]">x{summary.numcpu}</span>{" "}
471+
<span className="font-mono text-[11px]">{cpuPct}%</span>
472+
</span>
473+
</Tooltip>
474+
</>
475+
)}
476+
<span className="ml-auto whitespace-pre">
477+
Procs <span className="font-mono text-[11px]">{procCountValue}</span>
478+
</span>
479+
</div>
480+
);
481+
}
482+
483+
return (
484+
<div className="shrink-0 text-xs text-secondary border-b border-white/10 bg-panel flex items-center px-2 py-1">
485+
<div className="shrink-0 flex items-center mr-1">
486+
<StatusIndicator model={model} />
487+
</div>
488+
<div className="flex-1 max-w-3" />
489+
<div className="flex flex-row flex-1 min-w-0">
490+
{hasSummaryLoad && (
491+
<div className="flex flex-col shrink-0 w-[100px] mr-1">
492+
<div>Load</div>
493+
<div className="font-mono text-[11px] whitespace-pre">
494+
{fmtLoad(summary.load1)} {fmtLoad(summary.load5)} {fmtLoad(summary.load15)}
495+
</div>
496+
</div>
497+
)}
498+
{hasSummaryLoad && <div className="flex-1 max-w-3" />}
499+
{hasSummaryMem && (
500+
<div className="flex flex-col shrink-0 w-[95px] mr-1">
501+
<div>Mem</div>
502+
<div className="font-mono text-[11px] whitespace-pre">
503+
{memUsedFmt} / {memTotalFmt}
504+
</div>
505+
</div>
506+
)}
507+
{hasSummaryMem && <div className="flex-1 max-w-3" />}
508+
{hasSummaryCpu && (
509+
<div className="flex flex-col shrink-0 w-[55px] mr-1">
510+
<Tooltip content={`${summary.numcpu} cores`} placement="bottom">
511+
<div className="cursor-default">
512+
CPU<span className="font-mono text-[11px]">x{summary.numcpu}</span>
513+
</div>
514+
</Tooltip>
515+
<div className="font-mono text-[11px] whitespace-pre">{cpuPct}%</div>
516+
</div>
517+
)}
518+
{hasSummaryCpu && <div className="flex-1 max-w-3" />}
519+
<div className="flex-1" />
520+
<div className="flex flex-col w-[38px] shrink-0">
521+
<div>Procs</div>
522+
<div className="font-mono text-[11px] whitespace-pre">{procCountValue}</div>
523+
</div>
524+
</div>
525+
</div>
526+
);
527+
});
528+
StatusBar.displayName = "StatusBar";
529+
406530
export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewModel>> = React.memo(
407531
function ProcessViewerView({ blockId: _blockId, blockRef: _blockRef, contentRef: _contentRef, model }) {
408532
const data = jotai.useAtomValue(model.dataAtom);
@@ -411,111 +535,66 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
411535
const loading = jotai.useAtomValue(model.loadingAtom);
412536
const error = jotai.useAtomValue(model.errorAtom);
413537
const scrollTop = jotai.useAtomValue(model.scrollTopAtom);
414-
const scrollRef = React.useRef<HTMLDivElement>(null);
538+
const bodyScrollRef = React.useRef<HTMLDivElement>(null);
415539
const containerRef = React.useRef<HTMLDivElement>(null);
540+
const [wide, setWide] = React.useState(false);
416541

542+
const platform = data?.platform ?? "";
543+
const startIdx = Math.max(0, Math.floor(scrollTop / RowHeight) - OverscanRows);
417544
const totalCount = data?.totalcount ?? 0;
418-
const filteredCount = data?.filteredcount ?? 0;
419545
const processes = data?.processes ?? [];
420546
const hasCpu = data?.hascpu ?? false;
421-
const platform = data?.platform ?? "";
422-
const startIdx = Math.max(0, Math.floor(scrollTop / RowHeight) - OverscanRows);
423547

424-
// track container height
425548
React.useEffect(() => {
426549
const el = containerRef.current;
427550
if (!el) return;
428551
const ro = new ResizeObserver((entries) => {
429552
for (const entry of entries) {
430553
model.setContainerHeight(entry.contentRect.height);
554+
setWide(entry.contentRect.width >= 600);
431555
}
432556
});
433557
ro.observe(el);
434558
model.setContainerHeight(el.clientHeight);
559+
setWide(el.clientWidth >= 600);
435560
return () => ro.disconnect();
436561
}, [model]);
437562

438563
const handleScroll = React.useCallback(() => {
439-
const el = scrollRef.current;
564+
const el = bodyScrollRef.current;
440565
if (!el) return;
441566
model.setScrollTop(el.scrollTop);
442567
}, [model]);
443568

444569
const totalHeight = totalCount * RowHeight;
445570
const paddingTop = startIdx * RowHeight;
446571

447-
const summary = data?.summary;
448-
const memUsedFmt = summary?.memused != null ? fmtMem(summary.memused) : null;
449-
const memTotalFmt = summary?.memtotal != null ? fmtMem(summary.memtotal) : null;
450-
const cpuPct =
451-
summary?.cpusum != null && summary?.numcpu != null && summary.numcpu > 0
452-
? (summary.cpusum / summary.numcpu).toFixed(1).padStart(6, " ")
453-
: null;
454-
455-
const procCountValue =
456-
totalCount > 0
457-
? filteredCount < totalCount
458-
? `${filteredCount} / ${totalCount}`
459-
: String(totalCount).padStart(5, " ")
460-
: loading
461-
? "…"
462-
: error
463-
? "Err"
464-
: "";
465-
466-
const hasSummaryLoad = summary != null && summary.load1 != null;
467-
const hasSummaryMem = summary != null && memUsedFmt != null;
468-
const hasSummaryCpu = summary != null && cpuPct != null;
469-
470572
return (
471573
<div className="flex flex-col w-full h-full overflow-hidden" ref={containerRef}>
472-
{/* status bar */}
473-
<div className="shrink-0 text-xs text-secondary border-b border-white/10 bg-panel">
474-
<div className="flex items-center gap-4 px-2 pt-1 pb-0">
475-
<StatusIndicator model={model} />
476-
{hasSummaryLoad && <span className="w-[120px] shrink-0">Load</span>}
477-
{hasSummaryMem && <span className="w-[120px] shrink-0">Mem</span>}
478-
{hasSummaryCpu && (
479-
<Tooltip content={`${summary.numcpu} cores`} placement="bottom">
480-
<span className="w-[70px] shrink-0 cursor-default">
481-
CPU<span className="font-mono text-[11px]">x{summary.numcpu}</span>
482-
</span>
483-
</Tooltip>
484-
)}
485-
<span className="ml-auto">Procs</span>
486-
</div>
487-
<div className="flex items-center gap-4 px-2 pb-1 pt-0">
488-
<div className="w-4 shrink-0" />
489-
{hasSummaryLoad && (
490-
<span className="font-mono text-[11px] w-[120px] shrink-0 whitespace-pre">
491-
{fmtLoad(summary.load1)} {fmtLoad(summary.load5)} {fmtLoad(summary.load15)}
492-
</span>
493-
)}
494-
{hasSummaryMem && (
495-
<span className="font-mono text-[11px] w-[120px] shrink-0 whitespace-pre">
496-
{memUsedFmt} / {memTotalFmt}
497-
</span>
498-
)}
499-
{hasSummaryCpu && (
500-
<span className="font-mono text-[11px] w-[70px] shrink-0 whitespace-pre">{cpuPct}%</span>
501-
)}
502-
<span className="ml-auto font-mono text-[11px] whitespace-pre">{procCountValue}</span>
503-
</div>
504-
</div>
574+
<StatusBar model={model} data={data} loading={loading} error={error} wide={wide} />
505575

506576
{/* error */}
507577
{error != null && <div className="px-3 py-2 text-xs text-error shrink-0">{error}</div>}
508578

509-
{/* header */}
510-
<TableHeader model={model} sortBy={sortBy} sortDesc={sortDesc} platform={platform} />
511-
512-
{/* virtualized rows */}
513-
<div ref={scrollRef} className="flex-1 overflow-y-auto overflow-x-auto" onScroll={handleScroll}>
514-
<div style={{ height: totalHeight, position: "relative", minWidth: "100%" }}>
515-
<div style={{ position: "absolute", top: paddingTop, left: 0, right: 0, minWidth: "100%" }}>
516-
{processes.map((proc) => (
517-
<ProcessRow key={proc.pid} proc={proc} hasCpu={hasCpu} platform={platform} />
518-
))}
579+
{/* outer h-scroll wrapper */}
580+
<div className="flex-1 overflow-x-auto overflow-y-hidden">
581+
{/* inner column — expands to header's natural width, rows match */}
582+
<div className="flex flex-col h-full min-w-full w-max">
583+
<TableHeader model={model} sortBy={sortBy} sortDesc={sortDesc} platform={platform} />
584+
585+
{/* virtualized rows — same width as header, scrolls vertically */}
586+
<div
587+
ref={bodyScrollRef}
588+
className="flex-1 overflow-y-auto overflow-x-hidden w-full"
589+
onScroll={handleScroll}
590+
>
591+
<div style={{ height: totalHeight, position: "relative" }}>
592+
<div style={{ position: "absolute", top: paddingTop, left: 0, right: 0 }}>
593+
{processes.map((proc) => (
594+
<ProcessRow key={proc.pid} proc={proc} hasCpu={hasCpu} platform={platform} />
595+
))}
596+
</div>
597+
</div>
519598
</div>
520599
</div>
521600
</div>

0 commit comments

Comments
 (0)