diff --git a/AppImage/ProxMenux-1.2.2.AppImage b/AppImage/ProxMenux-1.2.2.1-beta.AppImage similarity index 65% rename from AppImage/ProxMenux-1.2.2.AppImage rename to AppImage/ProxMenux-1.2.2.1-beta.AppImage index 74c81a32..a05d2628 100755 Binary files a/AppImage/ProxMenux-1.2.2.AppImage and b/AppImage/ProxMenux-1.2.2.1-beta.AppImage differ diff --git a/AppImage/ProxMenux-Monitor.AppImage.sha256 b/AppImage/ProxMenux-Monitor.AppImage.sha256 index 0a4c8f89..52b3ed1f 100644 --- a/AppImage/ProxMenux-Monitor.AppImage.sha256 +++ b/AppImage/ProxMenux-Monitor.AppImage.sha256 @@ -1 +1 @@ -e0128ac327ea74b645b37bcfab03aa744f067a355f9960a822b411c6ac75cb02 ProxMenux-1.2.2.AppImage +8ea86a03ea86d45050d4e24e6432e022f76d805eeaaeeab3ee376ebaa48da52a ProxMenux-1.2.2.1-beta.AppImage diff --git a/AppImage/components/process-info-modal.tsx b/AppImage/components/process-info-modal.tsx index 2345cc42..11a83f33 100644 --- a/AppImage/components/process-info-modal.tsx +++ b/AppImage/components/process-info-modal.tsx @@ -1,9 +1,9 @@ "use client" -import { useEffect, useState } from "react" +import { useEffect, useRef, useState } from "react" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog" import { ScrollArea } from "./ui/scroll-area" -import { Activity, FileText, HardDrive, Clock } from "lucide-react" +import { Activity, FileText, HardDrive, Clock, Info } from "lucide-react" import { fetchApi } from "@/lib/api-config" interface ProcessDetail { @@ -78,8 +78,17 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps const [data, setData] = useState(null) const [error, setError] = useState(null) const [loading, setLoading] = useState(false) + const [exited, setExited] = useState(false) + const intervalRef = useRef | null>(null) const open = pid != null + const stopPolling = () => { + if (intervalRef.current) { + clearInterval(intervalRef.current) + intervalRef.current = null + } + } + const fetchDetail = async (silent = false) => { if (pid == null) return if (!silent) setLoading(true) @@ -88,11 +97,16 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps const res = await fetchApi(`/api/processes/${pid}`) setData(res) } catch (e: any) { - // 404 means the process exited while the modal was open — surface a - // clear message instead of stale data, but don't auto-close (user may - // want to read the last snapshot). - setError(e?.message?.includes("404") ? "Process exited" : (e?.message || "Failed to fetch process")) - if (e?.message?.includes("404")) setData(null) + // 404 = the process exited while the modal was open. Expected for + // short-lived helpers (pct exec, backup subprocesses, the `ps` snapshot + // itself). Keep the last good snapshot on screen, stop polling, and + // surface an info banner — NOT an error — so it doesn't look like a bug. + if (e?.message?.includes("404")) { + setExited(true) + stopPolling() + } else { + setError(e?.message || "Failed to fetch process") + } } finally { if (!silent) setLoading(false) } @@ -102,11 +116,14 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps if (pid == null) { setData(null) setError(null) + setExited(false) + stopPolling() return } + setExited(false) fetchDetail() - const id = setInterval(() => fetchDetail(true), REFRESH_MS) - return () => clearInterval(id) + intervalRef.current = setInterval(() => fetchDetail(true), REFRESH_MS) + return () => stopPolling() // eslint-disable-next-line react-hooks/exhaustive-deps }, [pid]) @@ -123,10 +140,28 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps PID {pid} - Live snapshot from /proc/{pid}. Auto-refreshes every {REFRESH_MS / 1000} s while open. + {exited ? ( + <>Last snapshot from /proc/{pid} before the process finished. + ) : ( + <>Live snapshot from /proc/{pid}. Auto-refreshes every {REFRESH_MS / 1000} s while open. + )} + {/* Info banner when the process has finished. Amber, not red — this is + expected behavior for short-lived processes, not an error. */} + {exited && ( +
+ +
+
This process has finished
+
+ It was likely a short-lived helper (a script, a pct exec, or a one-shot command) that completed while the modal was open. The data below is the last snapshot captured before it exited — not a stale or broken read. +
+
+
+ )} + {error && !data ? (
{error}
) : !data ? ( @@ -134,11 +169,11 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps {loading ? "Loading…" : "—"} ) : ( - +
{/* Overview */}
} title="Overview"> - + @@ -176,7 +211,7 @@ export function ProcessInfoModal({ pid, accent, onClose }: ProcessInfoModalProps {data?.captured_at && (
- Captured {new Date(data.captured_at * 1000).toLocaleTimeString()} + {exited ? "Last seen" : "Captured"} {new Date(data.captured_at * 1000).toLocaleTimeString()} {error ? ` · ${error}` : ""}
)} diff --git a/AppImage/components/release-notes-modal.tsx b/AppImage/components/release-notes-modal.tsx index 7b8a8ac6..8180df37 100644 --- a/AppImage/components/release-notes-modal.tsx +++ b/AppImage/components/release-notes-modal.tsx @@ -216,17 +216,37 @@ export const CHANGELOG: Record = { } const CURRENT_VERSION_FEATURES = [ + { + icon: , + text: "Dashboard header restyle - The Overview, Storage, Network and VMs & LXCs tabs now lead with a unified card design: circular gauges paired with mini progress bars, and a clearer headline-plus-pill layout for counts. CPU Usage and Memory cards also expose a User / System and Used / Cached breakdown under the gauge", + }, { icon: , - text: "Header Critical badge now respects dismissals (#228) - Permanently silencing every critical alert in a category used to leave the badge stuck on Critical even though the popup correctly reported 0 critical. The rollup that drives /api/system-info now runs a dismiss-aware pass over every category, so the badge, the popup and any API consumer all see the same view", + text: "Top processes drill-down - Click the CPU Usage or Memory cards on the Overview tab to open a sortable top-25 list of processes by %CPU or RSS, then click any row for a live per-process detail modal with state, command line, resource usage and lifetime", + }, + { + icon: , + text: "Storage and Network refinements - Storage Used now leads the Storage tab card, Network Status cells stack label-over-value so long hostnames and IPv6 DNS no longer truncate, and the 24 h CPU and Network charts smooth into 5-minute buckets", + }, + { + icon: , + text: "Coral on non-Debian LXCs - The Coral LXC installer now detects Alpine / Arch / RHEL / SUSE containers and offers an opt-in passthrough-only mode, so Frigate Docker and other apps bundling their own libedgetpu runtime can wire the device through without aborting", + }, + { + icon: , + text: "Coral PCIe driver false-positive update fix - The installer now records the upstream feranick release tag on disk, so the update detector compares like-for-like and stops firing a phantom 'driver update available' notification right after a fresh install", }, { icon: , - text: "Auto-reconcile of stale alerts - Errors for resources that no longer exist now auto-clear within the regular cleanup cycle. New cases: a PVE storage removed via pvesm, an NFS/CIFS share whose mount target is no longer in /proc/mounts (the lazy-umount case reported in the field), and LXC mount-capacity alerts whose CT has been deleted", + text: "Auto-reconcile of stale alerts - Errors for resources that no longer exist auto-clear within the regular cleanup cycle. New cases: a PVE storage removed via pvesm, an NFS/CIFS share whose mount target is gone from /proc/mounts, and LXC mount-capacity alerts for a deleted CT", + }, + { + icon: , + text: "Header Critical badge respects dismissals (#228) - Permanently silencing every critical alert in a category now also clears the top-right badge, so the header pill and the bottom popup always agree on the count", }, { icon: , - text: "Notification Send Test buttons unified (#226) - All five channel Send Test buttons (Telegram, Gotify, Discord, Email, Apprise) now sit on the left side and carry their channel's brand colour with white text, instead of Apprise being the right-aligned cyan outlier", + text: "Notification Send Test buttons unified (#226) - All five channel Send Test buttons (Telegram, Gotify, Discord, Email, Apprise) now sit on the left and carry their channel's brand colour, instead of Apprise being the right-aligned cyan outlier", }, ] diff --git a/beta_version.txt b/beta_version.txt index 93f340a7..7fd30986 100644 --- a/beta_version.txt +++ b/beta_version.txt @@ -1 +1 @@ -1.2.1.4 +1.2.2.1 diff --git a/web/messages/en/docs/monitor/dashboard/system-overview.json b/web/messages/en/docs/monitor/dashboard/system-overview.json index f179fffc..ca09a3b0 100644 --- a/web/messages/en/docs/monitor/dashboard/system-overview.json +++ b/web/messages/en/docs/monitor/dashboard/system-overview.json @@ -73,7 +73,7 @@ "Command — short name (comm), full command line, executable path and working directory.", "Lifetime — start timestamp and elapsed runtime." ], - "detailRefresh": "The detail modal refreshes every 3 s while open. If the process exits mid-modal the next refresh surfaces Process exited instead of stale data — expected for short-lived helpers like pct exec or backup subprocesses.", + "detailRefresh": "The detail modal refreshes every 3 s while open. If the process exits mid-modal the polling stops, an amber This process has finished banner appears, and the last captured snapshot stays on screen (dimmed) so you can still read what was happening just before it exited — expected for short-lived helpers like pct exec, backup subprocesses or the underlying ps snapshot itself.", "captureDetailAlt": "Process detail modal — Overview, Resources, Command and Lifetime sections for a single PID", "captureDetailCaption": "Per-process detail modal opened from a list row. The accent colour matches the card that opened it (blue for CPU, indigo for Memory).", "sourceTitle": "Where the data comes from", diff --git a/web/messages/es/docs/monitor/dashboard/system-overview.json b/web/messages/es/docs/monitor/dashboard/system-overview.json index f69eadde..556b5a2f 100644 --- a/web/messages/es/docs/monitor/dashboard/system-overview.json +++ b/web/messages/es/docs/monitor/dashboard/system-overview.json @@ -73,7 +73,7 @@ "Command — nombre corto (comm), línea de comandos completa, ruta del ejecutable y directorio de trabajo.", "Lifetime — timestamp de arranque y tiempo transcurrido en ejecución." ], - "detailRefresh": "La modal de detalle se refresca cada 3 s mientras está abierta. Si el proceso termina con la modal abierta, el siguiente refresco muestra Process exited en vez de datos obsoletos — esperable para procesos efímeros como pct exec o subprocesos de backup.", + "detailRefresh": "La modal de detalle se refresca cada 3 s mientras está abierta. Si el proceso termina con la modal abierta, el polling se detiene, aparece un banner ámbar This process has finished y el último snapshot capturado se queda en pantalla (atenuado) para que sigas viendo qué estaba pasando justo antes de que terminara — esperable para procesos efímeros como pct exec, subprocesos de backup o el propio ps que alimenta la lista.", "captureDetailAlt": "Modal de detalle de proceso — secciones Overview, Resources, Command y Lifetime para un único PID", "captureDetailCaption": "Modal de detalle por proceso abierta desde una fila de la lista. El color de acento sigue al de la tarjeta que la abrió (azul para CPU, índigo para Memory).", "sourceTitle": "De dónde salen los datos",