diff --git a/apps/docs/docs/widgets/beszel-system-grid/index.mdx b/apps/docs/docs/widgets/beszel-system-grid/index.mdx
index fd4232524..993974914 100644
--- a/apps/docs/docs/widgets/beszel-system-grid/index.mdx
+++ b/apps/docs/docs/widgets/beszel-system-grid/index.mdx
@@ -4,8 +4,6 @@ description: "Card grid view of all Beszel-monitored systems with real-time metr
hide_title: true
---
-import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -36,8 +34,6 @@ Select a system card to open the complete statistics view, including system, fil
]}
/>
-
-
### Adding the widget
diff --git a/apps/docs/docs/widgets/beszel-system-table/index.mdx b/apps/docs/docs/widgets/beszel-system-table/index.mdx
index aa253e6d3..e1bbd8c93 100644
--- a/apps/docs/docs/widgets/beszel-system-table/index.mdx
+++ b/apps/docs/docs/widgets/beszel-system-table/index.mdx
@@ -15,9 +15,9 @@ import { beszelIntegration } from "@site/docs/integrations/beszel";
-This widget displays all your Beszel-monitored systems in a sortable data table. Each row shows system metrics like CPU, memory, disk, network, temperature, and more. Columns can be toggled on or off, dragged into a different order, and resized from their headers. Column order and widths are saved automatically for each widget. When Beszel reports extra filesystems, the disk column adds a hover breakdown for each mount alongside the root disk.
+This widget displays all your Beszel-monitored systems in a sortable data table. Each row shows system metrics like CPU, memory, disk, network, temperature, and more. System and load-average status dots are displayed inline with their values. Columns can be toggled on or off, dragged into a different order, and resized from their headers. Column order and widths are saved automatically for each widget. When Beszel reports extra filesystems, the disk column adds a hover breakdown for each mount alongside the root disk.
-Select a system row to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
+Select a system row to open the complete statistics view.
### Interactions
@@ -37,7 +37,7 @@ Select a system row to open the complete statistics view, including system, file
]}
/>
-
+
### Adding the widget
diff --git a/apps/nextjs/src/components/board/advanced-focus/advanced-focus.module.css b/apps/nextjs/src/components/board/advanced-focus/advanced-focus.module.css
index 60cc31954..c108d72c8 100644
--- a/apps/nextjs/src/components/board/advanced-focus/advanced-focus.module.css
+++ b/apps/nextjs/src/components/board/advanced-focus/advanced-focus.module.css
@@ -35,7 +35,7 @@
.surfaceHostAdvanced {
position: fixed;
inset: 0;
- z-index: calc(var(--mantine-z-index-modal) - 1);
+ z-index: calc(var(--mantine-z-index-popover) - 1);
width: auto;
height: auto;
pointer-events: none;
diff --git a/apps/nextjs/src/components/board/advanced-focus/context.tsx b/apps/nextjs/src/components/board/advanced-focus/context.tsx
index 3f38bdc44..4a5935efe 100644
--- a/apps/nextjs/src/components/board/advanced-focus/context.tsx
+++ b/apps/nextjs/src/components/board/advanced-focus/context.tsx
@@ -201,7 +201,7 @@ export const BoardAdvancedFocusProvider = ({ children }: PropsWithChildren) => {
data-advanced-focus-overlay
backgroundOpacity={0.38}
blur={0}
- zIndex="calc(var(--mantine-z-index-modal) - 2)"
+ zIndex="calc(var(--mantine-z-index-popover) - 2)"
className={`${classes.backdrop} ${active.phase === "closing" ? classes.backdropClosing : ""}`}
aria-hidden
/>
diff --git a/apps/nextjs/src/components/board/advanced-focus/wheel.ts b/apps/nextjs/src/components/board/advanced-focus/wheel.ts
new file mode 100644
index 000000000..4cf5bc36d
--- /dev/null
+++ b/apps/nextjs/src/components/board/advanced-focus/wheel.ts
@@ -0,0 +1,30 @@
+const canScrollVertically = (element: HTMLElement, delta: number) => {
+ if (element.scrollHeight <= element.clientHeight) return false;
+
+ const overflowY = getComputedStyle(element).overflowY;
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
+
+ return delta < 0 ? element.scrollTop > 0 : element.scrollTop + element.clientHeight < element.scrollHeight;
+};
+
+export const redirectShiftWheel = (root: HTMLElement, target: EventTarget | null, delta: number) => {
+ if (delta === 0) return false;
+
+ let current = target instanceof HTMLElement ? target : null;
+ while (current && root.contains(current)) {
+ if (canScrollVertically(current, delta)) {
+ current.scrollTop += delta;
+ return true;
+ }
+ if (current === root) break;
+ current = current.parentElement;
+ }
+
+ const viewport = [...root.querySelectorAll("[data-scrollbars='y'], [data-scrollbars='xy']")].find(
+ (element) => canScrollVertically(element, delta),
+ );
+ if (!viewport) return false;
+
+ viewport.scrollTop += delta;
+ return true;
+};
diff --git a/e2e/board-advanced-interactions.spec.ts b/e2e/board-advanced-interactions.spec.ts
index eabb0a33f..2a2a32303 100644
--- a/e2e/board-advanced-interactions.spec.ts
+++ b/e2e/board-advanced-interactions.spec.ts
@@ -115,6 +115,18 @@ describe("Board advanced interactions", () => {
await expect(page.getByRole("button", { name: /advanced view is pinned|keep advanced view open/i })).toHaveCount(
0,
);
+ await advancedSurface.evaluate((element) => {
+ const probe = document.createElement("div");
+ probe.dataset.shiftScrollProbe = "true";
+ probe.style.cssText = "height:80px;overflow-y:auto";
+ probe.innerHTML = '';
+ element.append(probe);
+ });
+ const scrollProbe = advancedSurface.locator("[data-shift-scroll-probe]");
+ await page.keyboard.down("Shift");
+ await scrollProbe.dispatchEvent("wheel", { shiftKey: true, deltaX: 120, deltaY: 0 });
+ await page.keyboard.up("Shift");
+ expect(await scrollProbe.evaluate((element) => element.scrollTop)).toBeGreaterThan(0);
await page.keyboard.press("Escape");
await expect(advancedSurface).toBeHidden();
await expect(widget).toBeFocused();
diff --git a/packages/integrations/src/interfaces/media-server/media-server-types.ts b/packages/integrations/src/interfaces/media-server/media-server-types.ts
index c9a287155..42b6f0615 100644
--- a/packages/integrations/src/interfaces/media-server/media-server-types.ts
+++ b/packages/integrations/src/interfaces/media-server/media-server-types.ts
@@ -1,7 +1,7 @@
export interface StreamSession {
sessionId: string;
sessionName: string;
- user: {
+ user?: {
userId: string;
username: string;
profilePictureUrl: string | null;
diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json
index b207e11bd..2430b51dd 100644
--- a/packages/translation/src/lang/en.json
+++ b/packages/translation/src/lang/en.json
@@ -3488,6 +3488,7 @@
"items": {
"currentlyPlaying": "Currently playing",
"user": "User",
+ "unknownUser": "Unknown user",
"name": "Name",
"id": "Id",
"status": "Status",
diff --git a/packages/widgets/src/app/index.ts b/packages/widgets/src/app/index.ts
index 2b03f0e99..7b8416ca9 100644
--- a/packages/widgets/src/app/index.ts
+++ b/packages/widgets/src/app/index.ts
@@ -15,6 +15,7 @@ import { optionsBuilder } from "../options";
export const { definition, componentLoader } = createWidgetDefinition("app", {
icon: IconApps,
+ supportsAdvancedFocus: false,
queryKeys: [[["app", "byId"]], [["widget", "app", "ping"]]],
createOptions(settings) {
return optionsBuilder.from(
diff --git a/packages/widgets/src/beszel-system-grid/index.ts b/packages/widgets/src/beszel-system-grid/index.ts
index 9a5444e28..b3ef14806 100644
--- a/packages/widgets/src/beszel-system-grid/index.ts
+++ b/packages/widgets/src/beszel-system-grid/index.ts
@@ -6,6 +6,7 @@ import { optionsBuilder } from "../options";
export const { definition, componentLoader } = createWidgetDefinition("beszelSystemGrid", {
icon: IconLayoutGrid,
+ supportsAdvancedFocus: false,
queryKey: [["widget", "beszel", "getSystems"]],
supportedIntegrations: ["beszel", "mock"],
integrationsRequired: true,
diff --git a/packages/widgets/src/beszel-system-table/index.ts b/packages/widgets/src/beszel-system-table/index.ts
index 76fb0264f..4007bebc0 100644
--- a/packages/widgets/src/beszel-system-table/index.ts
+++ b/packages/widgets/src/beszel-system-table/index.ts
@@ -25,6 +25,7 @@ const sortDirectionOptions = [
export const { definition, componentLoader } = createWidgetDefinition("beszelSystemTable", {
icon: IconTable,
+ supportsAdvancedFocus: false,
queryKeys: [[["widget", "beszel", "getSystems"]], [["widget", "beszel", "getSystemStats"]]],
queryMatcher: (query, scope) =>
query.path.at(-1) === "getSystems"
diff --git a/packages/widgets/src/beszel/_shared/disk-usage.tsx b/packages/widgets/src/beszel/_shared/disk-usage.tsx
index 9aa2a14ad..826d5c3c8 100644
--- a/packages/widgets/src/beszel/_shared/disk-usage.tsx
+++ b/packages/widgets/src/beszel/_shared/disk-usage.tsx
@@ -1,4 +1,4 @@
-import { Box, Group, HoverCard, Progress, Stack, Text, UnstyledButton } from "@mantine/core";
+import { Box, getDefaultZIndex, Group, HoverCard, Progress, Stack, Text, UnstyledButton } from "@mantine/core";
import type { BeszelSystemRow } from "./types";
import { thresholdColor } from "./colors";
@@ -28,6 +28,7 @@ export const DiskUsage = ({ system, fontSize, progressSize, valueMiw, valueGap =
{formatPercent(system.disk)}
undefined;
@@ -32,3 +40,22 @@ describe("getWidgetQueryKeys", () => {
expect(getWidgetRuntimeQueries(widgetStateRef)).toEqual(runtimeQueries);
});
});
+
+describe("supportsAdvancedFocus", () => {
+ const definition = { supportsAdvancedFocus: undefined } as Parameters[0];
+
+ test("defaults to enabled", () => {
+ expect(supportsAdvancedFocus(definition)).toBe(true);
+ });
+
+ test("allows widgets to opt out", () => {
+ expect(supportsAdvancedFocus({ ...definition, supportsAdvancedFocus: false })).toBe(false);
+ });
+
+ test.each([appDefinition, beszelSystemGridDefinition, beszelSystemTableDefinition])(
+ "keeps non-enhanced widgets compact",
+ (widgetDefinition) => {
+ expect(supportsAdvancedFocus(widgetDefinition)).toBe(false);
+ },
+ );
+});
diff --git a/packages/widgets/src/index.tsx b/packages/widgets/src/index.tsx
index ba5aad490..879befb72 100644
--- a/packages/widgets/src/index.tsx
+++ b/packages/widgets/src/index.tsx
@@ -79,6 +79,7 @@ export {
getWidgetQueryKeys,
getWidgetRuntimeQueries,
normalizeWidgetQuery,
+ supportsAdvancedFocus,
widgetQueryValueEquals,
} from "./definition";
export type { WidgetComponentProps };