= { btop: Cpu, pm2: Server, docker: Boxes };
-export const ScopeList = ({ panelId: _panelId }: { panelId: string }) => {
- const [scope, setScope] = useMonitorScope();
- return (
-
- {SCOPES.map((s) => {
- const Icon = ICONS[s.id];
- return (
- setScope(s.id)}
- className={`flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm ${
- scope === s.id ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'
- }`}
- >
-
- {s.label}
-
- );
- })}
-
- );
-};
+export const ScopeList = ({ panelId: _panelId }: { panelId: string }) => (
+
+ {SCOPES.map((s) => {
+ const Icon = ICONS[s.id];
+ return (
+
+ `flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm ${
+ isActive ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'
+ }`
+ }
+ >
+
+ {s.label}
+
+ );
+ })}
+
+);
diff --git a/src/workspaces/officerdev/src/apps/SystemMonitor/shared.ts b/src/workspaces/officerdev/src/apps/SystemMonitor/shared.ts
index 02959c4d..943984e5 100644
--- a/src/workspaces/officerdev/src/apps/SystemMonitor/shared.ts
+++ b/src/workspaces/officerdev/src/apps/SystemMonitor/shared.ts
@@ -1,18 +1,30 @@
-import { usePanelChannel } from 'hooks/usePanelChannel';
+import { useParams } from 'react-router';
-// The left panel picks a scope; the right panel renders it. Coordinated over a panel channel, like the
-// /music browser → detail split.
+// The left panel picks a scope and the right panel renders it, and neither tells the other: the scope is
+// `/system-monitor/:scope`. It used to be a `monitor:scope` panel channel, which meant the URL said
+// `/system-monitor` however deep you were — nothing to link, nothing to bookmark, and the list came back
+// on whatever you last clicked in some other tab.
export type MonitorScope = 'btop' | 'pm2' | 'docker';
-export const MONITOR_SCOPE_CHANNEL = 'monitor:scope';
-
export const SCOPES: { id: MonitorScope; label: string }[] = [
{ id: 'btop', label: 'bTop' },
{ id: 'pm2', label: 'pm2 processes' },
{ id: 'docker', label: 'dockers' },
];
-export const useMonitorScope = () => usePanelChannel(MONITOR_SCOPE_CHANNEL, 'btop');
+/** Where /system-monitor lands, and where an unrecognised scope redirects to. */
+export const DEFAULT_MONITOR_SCOPE: MonitorScope = 'btop';
+
+export const isMonitorScope = (value: string | undefined): value is MonitorScope => SCOPES.some((s) => s.id === value);
+
+export const monitorScopePath = (scope: MonitorScope) => `/system-monitor/${scope}`;
+
+// SystemMonitorScreen redirects anything unrecognised, so the fallback here only covers the instant before
+// that lands.
+export const useMonitorScope = (): MonitorScope => {
+ const { scope } = useParams();
+ return isMonitorScope(scope) ? scope : DEFAULT_MONITOR_SCOPE;
+};
export function fmtBytes(n: number): string {
if (!n) return '0 B';
diff --git a/src/workspaces/officerdev/src/index.ts b/src/workspaces/officerdev/src/index.ts
index 70fba8ab..a8dd2596 100644
--- a/src/workspaces/officerdev/src/index.ts
+++ b/src/workspaces/officerdev/src/index.ts
@@ -43,6 +43,10 @@ export type { PhotosSectionId } from './apps/Photos/shared';
export { DEFAULT_JELLYFIN_SECTION, jellyfinSectionPath, isJellyfinSection } from './apps/Jellyfin/shared';
export type { JellyfinSectionId } from './apps/Jellyfin/shared';
+// Same for /system-monitor, where the param is the scope rather than a section.
+export { DEFAULT_MONITOR_SCOPE, monitorScopePath, isMonitorScope } from './apps/SystemMonitor/shared';
+export type { MonitorScope } from './apps/SystemMonitor/shared';
+
// Same for /transmission.
export {
DEFAULT_TRANSMISSION_SECTION,