@@ -199,6 +229,27 @@ export default function SystemResources({
const data = results.filter(
(entry): entry is typeof entry & { healthInfo: NonNullable
} => entry.healthInfo !== null,
);
+ const disks = data.flatMap((entry) => {
+ const fileSystem = filterStorageVolumes(
+ entry.healthInfo.fileSystem,
+ options.visibleStorageVolumes,
+ entry.integrationId,
+ );
+ const smart = filterStorageVolumes(entry.healthInfo.smart, options.visibleStorageVolumes, entry.integrationId);
+ return fileSystem.map((item) => ({
+ integrationId: entry.integrationId,
+ integrationName: entry.integrationName,
+ item,
+ smartItem: smart.find(
+ (candidate) => normalizeStorageDeviceName(candidate.deviceName) === normalizeStorageDeviceName(item.deviceName),
+ ),
+ }));
+ });
+ const isAdvanced = displayMode === "advanced";
+ const layout = useMemo(
+ () => getSystemDisksLayout(width, height, disks.length, isAdvanced),
+ [disks.length, height, isAdvanced, width],
+ );
const queryIndicators = (
@@ -217,40 +268,18 @@ export default function SystemResources({
if (isInitialWidgetQueryPending(healthQuery)) return ;
if (data.length === 0) return emptyState;
- const disks = data.flatMap((entry) => {
- const fileSystem = filterStorageVolumes(
- entry.healthInfo.fileSystem,
- options.visibleStorageVolumes,
- entry.integrationId,
- );
- const smart = filterStorageVolumes(entry.healthInfo.smart, options.visibleStorageVolumes, entry.integrationId);
- return fileSystem.map((item) => ({
- integrationId: entry.integrationId,
- integrationName: entry.integrationName,
- item,
- smartItem: smart.find(
- (candidate) => normalizeStorageDeviceName(candidate.deviceName) === normalizeStorageDeviceName(item.deviceName),
- ),
- }));
- });
-
const hasNoFileSystems = data.every((entry) => entry.healthInfo.fileSystem.length === 0);
const hasFailedSource = results.some((entry) => Boolean(entry.error));
if (hasNoFileSystems && !hasFailedSource && !healthQuery.error) throw new NoIntegrationDataError();
if (hasNoFileSystems || disks.length === 0) return emptyState;
- const isAdvanced = displayMode === "advanced";
- const minimumCardWidth = 260;
- const columns = Math.max(1, Math.min(disks.length, Math.floor(width / minimumCardWidth)));
- const cellWidth = width / columns;
-
return (
{queryIndicators}
-
+
{disks.map(({ integrationId, integrationName, item, smartItem }) => {
const freeText = t("status.free", {
percentage: String(Math.round(100 - clampPercentage(item.percentage))),
@@ -273,8 +302,8 @@ export default function SystemResources({
freeText={advancedDisplayTexts.free}
smartStatus={smartItem?.overallStatus}
isAdvanced={isAdvanced}
- showSecondaryText={isAdvanced || (height >= 160 && cellWidth >= 260)}
- showTemperature={isAdvanced || (height >= 100 && cellWidth >= 220)}
+ showSecondaryText={layout.showSecondaryText}
+ showTemperature={layout.showTemperature}
/>
);
})}
diff --git a/packages/widgets/src/system-resources/chart/common-chart.tsx b/packages/widgets/src/system-resources/chart/common-chart.tsx
index ffc10a45f..2c96818a5 100644
--- a/packages/widgets/src/system-resources/chart/common-chart.tsx
+++ b/packages/widgets/src/system-resources/chart/common-chart.tsx
@@ -2,7 +2,7 @@
import type { ReactNode } from "react";
import type { AreaChartSeries } from "@mantine/charts";
import { AreaChart, LineChart } from "@mantine/charts";
-import { Card, Center, Group, Stack, Text, useComputedColorScheme, useMantineTheme } from "@mantine/core";
+import { Card, Center, Group, Stack, Text, useMantineTheme } from "@mantine/core";
import { useElementSize, useHover, useMergedRef } from "@mantine/hooks";
import type { TooltipProps, YAxisProps } from "recharts";
@@ -36,13 +36,12 @@ export const CommonChart = ({
}) => {
const { ref: elementSizeRef, height } = useElementSize();
const theme = useMantineTheme();
- const colorScheme = useComputedColorScheme("light");
const board = useRequiredBoard();
const { hovered, ref: hoverRef } = useHover();
const ref = useMergedRef(elementSizeRef, hoverRef);
- const opacity = board.opacity / 100;
- const backgroundColor = colorScheme === "dark" ? `rgba(57, 57, 57, ${opacity})` : `rgba(246, 247, 248, ${opacity})`;
+ const backgroundColor = "rgb(from var(--mantine-color-primaryColor-filled) r g b / calc(var(--opacity, 1) * 0.12))";
+ const borderColor = "rgb(from var(--mantine-color-secondaryColor-filled) r g b / calc(var(--opacity, 1) * 0.45))";
const ChartComponent = chartType === "line" ? LineChart : AreaChart;
const showIcon = labelDisplayMode === "icon" || labelDisplayMode === "textWithIcon";
@@ -53,7 +52,7 @@ export const CommonChart = ({
ref={ref}
h={"100%"}
pos={"relative"}
- style={{ overflow: "visible" }}
+ style={{ overflow: "visible", border: `1px solid ${borderColor}` }}
p={0}
bg={backgroundColor}
radius={board.itemRadius}
@@ -119,7 +118,13 @@ export const CommonChart = ({
withYAxis={false}
withDots={false}
bg={backgroundColor}
- styles={{ root: { padding: 5, borderRadius: theme.radius[board.itemRadius] } }}
+ styles={{
+ root: {
+ padding: 5,
+ borderRadius: theme.radius[board.itemRadius],
+ backgroundColor,
+ },
+ }}
tooltipAnimationDuration={200}
tooltipProps={tooltipProps}
withTooltip={height >= 64}
diff --git a/packages/widgets/src/system-resources/component.tsx b/packages/widgets/src/system-resources/component.tsx
index 0aed6076a..65b8714b8 100644
--- a/packages/widgets/src/system-resources/component.tsx
+++ b/packages/widgets/src/system-resources/component.tsx
@@ -230,18 +230,19 @@ const SystemCharts = ({
const labelDisplayMode = isAdvanced ? "textWithIcon" : options.labelDisplayMode;
const chartCount = visibleCharts.length;
const chartColumns = isAdvanced && width >= 560 ? 2 : 1;
+ const compactChartGap = 8;
const chartHeight = isAdvanced
? 180
- : `calc((100% - ${Math.max(0, chartCount - 1) * 10}px) / ${Math.max(1, chartCount)})`;
+ : `calc((100% - ${Math.max(0, chartCount - 1) * compactChartGap}px) / ${Math.max(1, chartCount)})`;
return (
-
+
{showTitle && (
{integrationName}
)}
-
+
{chartCount === 0 && (
diff --git a/packages/widgets/src/tracearr/recent-activity-section.tsx b/packages/widgets/src/tracearr/recent-activity-section.tsx
index 81a6c3541..b52c5f1cc 100644
--- a/packages/widgets/src/tracearr/recent-activity-section.tsx
+++ b/packages/widgets/src/tracearr/recent-activity-section.tsx
@@ -35,7 +35,7 @@ export function RecentActivityList({
: session.mediaTitle;
return (
-
+
diff --git a/packages/widgets/src/tracearr/stats-section.tsx b/packages/widgets/src/tracearr/stats-section.tsx
index 4aa7d7378..94648306b 100644
--- a/packages/widgets/src/tracearr/stats-section.tsx
+++ b/packages/widgets/src/tracearr/stats-section.tsx
@@ -44,7 +44,7 @@ export function StatsBar({
function StatCard({ icon, label, value }: { icon: React.ReactNode; label: string; value: string | number }) {
return (
-
+
{icon}
diff --git a/packages/widgets/src/tracearr/streams-section.tsx b/packages/widgets/src/tracearr/streams-section.tsx
index d870582a1..6a4096146 100644
--- a/packages/widgets/src/tracearr/streams-section.tsx
+++ b/packages/widgets/src/tracearr/streams-section.tsx
@@ -65,7 +65,7 @@ function StreamCard({
: null;
return (
-
+
{stream.posterUrl && (
{violations.map((violation) => (
-
+
+
{viewMode !== "topPages" && (
+
{selectedView}
{advancedLists}
@@ -190,15 +191,17 @@ export function UmamiContent({
) : (
-
+
{selectedView}
- {advancedLists}
+
+ {advancedLists}
+
);
return (
-
+
{visitorStats.domain} ({formatTimeFrameLabel(timeFrame, t)})
@@ -264,7 +267,7 @@ export function UmamiContent({
)}
)}
-
+
{displayMode === "advanced" ? advancedContent : selectedView}
{(statsQuery.error || activeVisitorsQuery.error || multiEventQuery.error) && (
diff --git a/packages/widgets/src/umami/umami-top-list.tsx b/packages/widgets/src/umami/umami-top-list.tsx
index a9e86461b..ffb060134 100644
--- a/packages/widgets/src/umami/umami-top-list.tsx
+++ b/packages/widgets/src/umami/umami-top-list.tsx
@@ -5,6 +5,7 @@ import { Group, ScrollArea, Stack, Text } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import type { UmamiMetricItem } from "@homarr/integrations/types";
import { useCurrentIntlLocale, useI18n } from "@homarr/translation/client";
+import classes from "./component.module.css";
import { umamiQueryOptions } from "./umami-utils";
import { getUsableWidgetQueryData } from "../common/query-state";
@@ -52,7 +53,7 @@ function UmamiTopList({
const locale = useCurrentIntlLocale();
return (
-
+
{heading}
diff --git a/packages/widgets/src/ups/component.tsx b/packages/widgets/src/ups/component.tsx
index 7426b9705..055384821 100644
--- a/packages/widgets/src/ups/component.tsx
+++ b/packages/widgets/src/ups/component.tsx
@@ -35,6 +35,11 @@ const statusColors: Record = {
unknown: "gray",
};
+const neutralSurfaceBackground =
+ "rgb(from var(--mantine-color-default-hover) r g b / calc(var(--opacity, 1) * 0.12))";
+const neutralSurfaceBorder =
+ "rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45))";
+
type UpsLayout = "mini" | "compact" | "full";
export default function UpsWidget({
@@ -183,7 +188,7 @@ function UpsDeviceCard({ summary, sourceName, options, layout, advanced, t }: Up
if (layout === "mini") {
return (
-
+
@@ -226,13 +232,14 @@ function UpsDeviceCard({ summary, sourceName, options, layout, advanced, t }: Up
if (layout === "compact") {
return (
-
+
{showRing && summary.batteryCharge !== null && (
@@ -258,7 +265,7 @@ function UpsDeviceCard({ summary, sourceName, options, layout, advanced, t }: Up
}
return (
-
+
@@ -279,6 +286,7 @@ function UpsDeviceCard({ summary, sourceName, options, layout, advanced, t }: Up
size={68}
thickness={6}
roundCaps
+ rootColor={neutralSurfaceBorder}
sections={[{ value: clampPercent(summary.batteryCharge), color: getChargeColor(summary.batteryCharge) }]}
label={
@@ -301,7 +309,12 @@ function UpsDeviceCard({ summary, sourceName, options, layout, advanced, t }: Up
{Math.round(summary.load)}%
-
+
)}
diff --git a/packages/widgets/src/uptime-kuma/component.module.css b/packages/widgets/src/uptime-kuma/component.module.css
index dd8b8e19e..f56a12bda 100644
--- a/packages/widgets/src/uptime-kuma/component.module.css
+++ b/packages/widgets/src/uptime-kuma/component.module.css
@@ -16,7 +16,8 @@
gap: clamp(8px, 3cqi, 18px);
flex-shrink: 0;
padding-bottom: clamp(4px, 2cqi, 10px);
- border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
+ border-bottom: 1px solid
+ rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45));
}
.heroExpanded {
@@ -83,7 +84,9 @@
gap: clamp(2px, 1.5cqi, 5px);
padding: clamp(4px, 2.5cqi, 10px);
border-radius: var(--mantine-radius-md);
- background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
+ border: 1px solid
+ rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45));
+ background-color: transparent;
overflow: hidden;
}
diff --git a/packages/widgets/src/vpn/vpn-card.tsx b/packages/widgets/src/vpn/vpn-card.tsx
index 3229ab0e9..35fa3ae4d 100644
--- a/packages/widgets/src/vpn/vpn-card.tsx
+++ b/packages/widgets/src/vpn/vpn-card.tsx
@@ -19,6 +19,9 @@ import { getStatusColor, RUNNING_STATUS } from "./helpers";
export type VpnInfo = RouterOutputs["widget"]["vpn"]["getSummaries"][number]["summary"];
+const neutralBorderColor =
+ "rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45))";
+
export function VpnIntegrationCard({
vpn,
integrationName,
@@ -53,7 +56,14 @@ export function VpnIntegrationCard({
if (compact) {
return (
-
+
{content}
);
diff --git a/packages/widgets/src/weather/component.module.css b/packages/widgets/src/weather/component.module.css
index 2e2c76a7f..0b5645767 100644
--- a/packages/widgets/src/weather/component.module.css
+++ b/packages/widgets/src/weather/component.module.css
@@ -31,8 +31,8 @@
.dailyCard,
.detailMetric,
.solarDetails {
- background: color-mix(in srgb, var(--mantine-color-body) 72%, transparent);
- border: 0.0625rem solid color-mix(in srgb, var(--mantine-color-default-border) 72%, transparent);
+ background-color: transparent;
+ border: 0.0625rem solid rgb(from var(--mantine-color-secondaryColor-filled) r g b / calc(var(--opacity, 1) * 0.45));
}
.dailyCard {
@@ -43,8 +43,8 @@
.dailyCardButton:hover .dailyCard,
.dailyCardButton:focus-visible .dailyCard {
- background: color-mix(in srgb, var(--mantine-primary-color-light) 30%, var(--mantine-color-body));
- border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 45%, var(--mantine-color-default-border));
+ background-color: transparent;
+ border-color: rgb(from var(--mantine-color-secondaryColor-filled) r g b / calc(var(--opacity, 1) * 0.72));
}
.dailyDetailsDropdown {
@@ -74,7 +74,7 @@
}
.solarDetails {
- background: color-mix(in srgb, var(--mantine-color-yellow-light) 24%, var(--mantine-color-body));
+ background-color: transparent;
}
.detailsButton,
diff --git a/packages/widgets/src/wud/component.module.css b/packages/widgets/src/wud/component.module.css
index 1ab18a656..e83cd23eb 100644
--- a/packages/widgets/src/wud/component.module.css
+++ b/packages/widgets/src/wud/component.module.css
@@ -1,7 +1,3 @@
-[data-mantine-color-scheme="light"] .card {
- background-color: var(--mantine-color-gray-1);
-}
-
-[data-mantine-color-scheme="dark"] .card {
- background-color: var(--mantine-color-dark-7);
+.card {
+ background-color: transparent;
}
diff --git a/packages/widgets/src/wud/component.tsx b/packages/widgets/src/wud/component.tsx
index edd9197e1..933048dc7 100644
--- a/packages/widgets/src/wud/component.tsx
+++ b/packages/widgets/src/wud/component.tsx
@@ -206,14 +206,20 @@ const UpdateCard = ({
? t("updateAvailable")
: buildVersionText(truncateVersion(update.currentVersion), truncateVersion(update.newVersion));
const versionBadge = versionText ? (
-
+
{versionText}
) : null;
const showVersionTooltip = versionText !== null && !isDigestUpdate && versionText !== fullVersionText;
return (
-
+
{update.name}