Skip to content

Commit 1073243

Browse files
committed
updates for formatting
1 parent 74aaf92 commit 1073243

1 file changed

Lines changed: 92 additions & 7 deletions

File tree

frontend/app/view/processviewer/processviewer.tsx

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export class ProcessViewerViewModel implements ViewModel {
5959
containerHeightAtom: jotai.PrimitiveAtom<number>;
6060
loadingAtom: jotai.PrimitiveAtom<boolean>;
6161
errorAtom: jotai.PrimitiveAtom<string>;
62+
lastSuccessAtom: jotai.PrimitiveAtom<number>;
63+
pausedAtom: jotai.PrimitiveAtom<boolean>;
6264

6365
connection: jotai.Atom<string>;
6466
connStatus: jotai.Atom<ConnStatus>;
@@ -78,6 +80,8 @@ export class ProcessViewerViewModel implements ViewModel {
7880
this.containerHeightAtom = jotai.atom<number>(0);
7981
this.loadingAtom = jotai.atom<boolean>(true);
8082
this.errorAtom = jotai.atom<string>(null) as jotai.PrimitiveAtom<string>;
83+
this.lastSuccessAtom = jotai.atom<number>(0) as jotai.PrimitiveAtom<number>;
84+
this.pausedAtom = jotai.atom<boolean>(false) as jotai.PrimitiveAtom<boolean>;
8185

8286
this.connection = jotai.atom((get) => {
8387
const connValue = get(this.env.getBlockMetaKeyAtom(blockId, "connection"));
@@ -135,6 +139,7 @@ export class ProcessViewerViewModel implements ViewModel {
135139
globalStore.set(this.dataAtom, resp);
136140
globalStore.set(this.loadingAtom, false);
137141
globalStore.set(this.errorAtom, null);
142+
globalStore.set(this.lastSuccessAtom, Date.now());
138143
}
139144
(window as any).RPL = resp; // debugging (remove before commit)
140145
} catch (e) {
@@ -166,7 +171,21 @@ export class ProcessViewerViewModel implements ViewModel {
166171
this.cancelPoll();
167172
}
168173
this.cancelPoll = null;
169-
this.startPolling();
174+
if (!globalStore.get(this.pausedAtom)) {
175+
this.startPolling();
176+
}
177+
}
178+
179+
setPaused(paused: boolean) {
180+
globalStore.set(this.pausedAtom, paused);
181+
if (paused) {
182+
if (this.cancelPoll) {
183+
this.cancelPoll();
184+
}
185+
this.cancelPoll = null;
186+
} else {
187+
this.startPolling();
188+
}
170189
}
171190

172191
setSort(col: SortCol) {
@@ -244,6 +263,65 @@ const SortIndicator = React.memo(function SortIndicator({ active, desc }: { acti
244263
});
245264
SortIndicator.displayName = "SortIndicator";
246265

266+
const StatusIndicator = React.memo(function StatusIndicator({ model }: { model: ProcessViewerViewModel }) {
267+
const paused = jotai.useAtomValue(model.pausedAtom);
268+
const error = jotai.useAtomValue(model.errorAtom);
269+
const lastSuccess = jotai.useAtomValue(model.lastSuccessAtom);
270+
const [now, setNow] = React.useState(() => Date.now());
271+
272+
React.useEffect(() => {
273+
if (paused) return;
274+
const id = setInterval(() => setNow(Date.now()), 1000);
275+
return () => clearInterval(id);
276+
}, [paused]);
277+
278+
if (paused) {
279+
const tooltipContent = (
280+
<div className="flex flex-col gap-0.5">
281+
<span>Paused</span>
282+
<span className="text-muted">Click to resume</span>
283+
</div>
284+
);
285+
return (
286+
<Tooltip content={tooltipContent} placement="bottom">
287+
<div
288+
className="flex items-center justify-center w-4 h-4 cursor-pointer text-warning hover:opacity-80 transition-opacity"
289+
onClick={() => model.setPaused(false)}
290+
>
291+
<svg viewBox="0 0 16 16" fill="currentColor" className="w-3.5 h-3.5">
292+
<rect x="2" y="2" width="4" height="12" rx="1" />
293+
<rect x="10" y="2" width="4" height="12" rx="1" />
294+
</svg>
295+
</div>
296+
</Tooltip>
297+
);
298+
}
299+
300+
const stalled = lastSuccess > 0 && now - lastSuccess > 5000;
301+
const circleColor = error != null ? "text-error" : stalled ? "text-warning" : "text-success";
302+
const statusLabel = error != null ? "Error" : stalled ? "Stalled" : "Updating";
303+
const tooltipContent = (
304+
<div className="flex flex-col gap-0.5">
305+
<span>{statusLabel}</span>
306+
<span className="text-muted">Click to pause</span>
307+
</div>
308+
);
309+
310+
return (
311+
<Tooltip content={tooltipContent} placement="bottom">
312+
<div
313+
className={`flex items-center justify-center w-4 h-4 cursor-pointer ${circleColor} hover:opacity-80 transition-opacity`}
314+
onClick={() => model.setPaused(true)}
315+
>
316+
<svg viewBox="0 0 16 16" fill="currentColor" className="w-3 h-3">
317+
<circle cx="8" cy="8" r="6" />
318+
</svg>
319+
</div>
320+
</Tooltip>
321+
);
322+
});
323+
StatusIndicator.displayName = "StatusIndicator";
324+
247325
const TableHeader = React.memo(function TableHeader({
248326
model,
249327
sortBy,
@@ -358,33 +436,40 @@ export const ProcessViewerView: React.FC<ViewComponentProps<ProcessViewerViewMod
358436
const summary = data?.summary;
359437
const memUsedGb = summary?.memused != null ? (summary.memused / 1024 / 1024 / 1024).toFixed(1) : null;
360438
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;
439+
const cpuPct =
440+
summary?.cpusum != null && summary?.numcpu != null && summary.numcpu > 0
441+
? (summary.cpusum / summary.numcpu).toFixed(1).padStart(5, " ")
442+
: null;
362443

363444
return (
364445
<div className="flex flex-col w-full h-full overflow-hidden" ref={containerRef}>
365446
{/* status bar */}
366447
<div className="flex shrink-0 items-center gap-4 px-2 py-1 text-xs text-secondary border-b border-white/10 bg-panel">
448+
<StatusIndicator model={model} />
367449
{summary != null && (
368450
<>
369451
{summary.load1 != null && (
370452
<span>
371-
Load: {summary.load1.toFixed(2)} {summary.load5.toFixed(2)}{" "}
372-
{summary.load15.toFixed(2)}
453+
Load:{" "}
454+
<span className="font-mono">
455+
{summary.load1.toFixed(2)} {summary.load5.toFixed(2)}{" "}
456+
{summary.load15.toFixed(2)}
457+
</span>
373458
</span>
374459
)}
375460
{memUsedGb != null && (
376461
<span>
377-
Mem: {memUsedGb}G / {memTotalGb}G
462+
Mem: <span className="font-mono">{memUsedGb}G / {memTotalGb}G</span>
378463
</span>
379464
)}
380465
{cpuPct != null && (
381466
<span>
382-
CPU: {cpuPct}% ({summary.numcpu} cores)
467+
CPU: <span className="font-mono whitespace-pre">{cpuPct}% ({summary.numcpu} cores)</span>
383468
</span>
384469
)}
385470
</>
386471
)}
387-
<span className="ml-auto">
472+
<span className="ml-auto font-mono">
388473
{totalCount > 0
389474
? filteredCount < totalCount
390475
? `${filteredCount} / ${totalCount} processes`

0 commit comments

Comments
 (0)