Compare commits

...
Author SHA1 Message Date
ajnart 53fcc6997c style: auto-fix lint and format 2026-06-07 22:04:04 +00:00
Thomas Camlong 69e64356bb feat(widgets): add size-aware widget hook
Introduce useWidgetSize() hook and WidgetSizeProvider context that
exposes grid dimensions (gridWidth, gridHeight) to every widget,
enabling responsive rendering based on how many grid cells a widget
occupies.

Weather widget updated as proof of concept: when placed in a 1x1 or
1xN cell with forecast enabled, it switches to a compact layout that
hides the forecast row, wind speed, and city name to prevent clipping.
2026-06-08 00:02:57 +02:00
homarr-renovate[bot] f2705eeb57 chore(deps): update dependency oxfmt to ^0.53.0 (#5883)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 23:12:23 +00:00
homarr-renovate[bot] ac37d2c73f chore(deps): update react monorepo (#5886)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 21:13:36 +00:00
homarr-renovate[bot] e00ea624cd chore(deps): update nextjs monorepo to v16.2.7 (#5885)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 19:32:30 +00:00
homarr-renovate[bot] 20b62c42e3 chore(deps): update dependency rollup@>=4.0.0 <4.22.4 to >=4.61.0 (#5878)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 13:54:21 +00:00
homarr-renovate[bot] 59971991c7 chore(deps): update dependency vite@>=5.0.0 <=5.4.18 to >=8.0.16 (#5880)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 11:36:28 +00:00
homarr-renovate[bot] a629681ae5 chore(deps): update vitest monorepo to ^4.1.8 (#5879)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 09:34:48 +00:00
homarr-renovate[bot] 572d5fb5ec chore(deps): update dependency vite@>=5.0.0 <=5.4.18 to >=8.0.15 (#5877)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-06 07:01:54 +00:00
Thomasandhomarr-releases[bot] 91a1858743 chore: merge main into dev
Co-authored-by: homarr-releases[bot] <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-06-06 07:47:59 +02:00
homarr-renovate[bot] f00d3627c9 chore(deps): update dependency @gfazioli/mantine-onboarding-tour to ^4.0.3 (#5873)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-06-05 18:04:10 +00:00
homarr-releases[bot] 7760e01ccb chore: update bug report template (#5821)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-06-05 17:47:57 +00:00
9 changed files with 655 additions and 604 deletions

No files matched your search

+1
View File
@@ -33,6 +33,7 @@ body:
options: options:
# The below comment is used to insert a new version with on-release.yml # The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION# #NEXT_VERSION#
- 1.64.0
- 1.62.0 - 1.62.0
- 1.61.0 - 1.61.0
- 1.60.1-beta.1 - 1.60.1-beta.1
+24
View File
@@ -1,3 +1,27 @@
## [1.64.0](https://github.com/homarr-labs/homarr/compare/v1.63.0...v1.64.0) (2026-06-05)
### Features
* add comprehensive demo mode with mock integrations ([#5870](https://github.com/homarr-labs/homarr/issues/5870)) ([0a64679](https://github.com/homarr-labs/homarr/commit/0a6467979a5e3b3e3eae6252c31fca1d9af54dc1))
* add PeaNUT integration and UPS monitoring widget ([#5862](https://github.com/homarr-labs/homarr/issues/5862)) ([0dc2558](https://github.com/homarr-labs/homarr/commit/0dc2558fafedcbba246c2e968d00cc541ab9f84e))
* **docker:** add container logs modal and stats columns ([#4807](https://github.com/homarr-labs/homarr/issues/4807)) ([dbf021e](https://github.com/homarr-labs/homarr/commit/dbf021e04a785b44ad0e2ce45634de371d76de6d))
* media request search as first-class spotlight mode ([#5823](https://github.com/homarr-labs/homarr/issues/5823)) ([3d3e305](https://github.com/homarr-labs/homarr/commit/3d3e3059a98b36b6e64e3aad90f715e334baff3e))
* **onboarding:** enhance integration setup with Docker discovery and URL templating ([#5607](https://github.com/homarr-labs/homarr/issues/5607)) ([dd7a490](https://github.com/homarr-labs/homarr/commit/dd7a4908a36a71c1d0d1e41524eb8fd9bf53ca4d))
* **onboarding:** guided management and board tours ([#5756](https://github.com/homarr-labs/homarr/issues/5756)) ([0b663a7](https://github.com/homarr-labs/homarr/commit/0b663a7fc6c017b9fbe78c019f06d46ee95f165f))
* **widgets:** add in-place app editing in widget edit modal ([#5809](https://github.com/homarr-labs/homarr/issues/5809)) ([99346f2](https://github.com/homarr-labs/homarr/commit/99346f288bcfc701c41fecaab6f7bc0438d80563))
### Bug Fixes
* add bookmark widget option to toggle card borders ([#5854](https://github.com/homarr-labs/homarr/issues/5854)) ([41fe592](https://github.com/homarr-labs/homarr/commit/41fe5928f19207d02b04e64c2caac49d896f72ce))
* **auth:** relax password complexity requirements to suggestions-only ([#5764](https://github.com/homarr-labs/homarr/issues/5764)) ([fd10788](https://github.com/homarr-labs/homarr/commit/fd107888385557ededcf51576c6320a1960690f2))
* **db:** log when UNSAFE_ENABLE_MOCK_INTEGRATION is enabled during seeding ([efd37dc](https://github.com/homarr-labs/homarr/commit/efd37dca93be7f5f362331e9b5c2a95a87e24379))
* disable onboarding tours when demo mode is enabled ([68f24e3](https://github.com/homarr-labs/homarr/commit/68f24e34d580e3a155c37ca37c3ffd61f296bd0c))
* **docker:** restore container selection toolbar broken by Mantine v9 ([#5855](https://github.com/homarr-labs/homarr/issues/5855)) ([fe4598f](https://github.com/homarr-labs/homarr/commit/fe4598f7221a2861b8c5ae0cfd267e8c4fbf48df))
* **immich:** respect app color scheme in server-stats widget ([#5859](https://github.com/homarr-labs/homarr/issues/5859)) ([2c6b7a8](https://github.com/homarr-labs/homarr/commit/2c6b7a818e9d32b414b024d711a3b64533e61cb9))
* redirect to login after credentials logout instead of crashing on current page ([#5819](https://github.com/homarr-labs/homarr/issues/5819)) ([cc24f07](https://github.com/homarr-labs/homarr/commit/cc24f070db26337a4a2d87d68c7f142ed1118584))
* sync docs sitemap and remove stale coolify ts-expect-error ([4f5f3e1](https://github.com/homarr-labs/homarr/commit/4f5f3e14a129a94295d4b8ffb7d3702fdb4f6a74))
* **ui:** restore pre-v9 light variant colors ([#5856](https://github.com/homarr-labs/homarr/issues/5856)) ([a755692](https://github.com/homarr-labs/homarr/commit/a755692d81b80b5ff83051a3b7261c01ac10321b))
## [1.63.0](https://github.com/homarr-labs/homarr/compare/v1.62.0...v1.63.0) (2026-05-29) ## [1.63.0](https://github.com/homarr-labs/homarr/compare/v1.62.0...v1.63.0) (2026-05-29)
### Features ### Features
@@ -1,6 +1,7 @@
import { useMemo } from "react"; import { useMemo } from "react";
import type { GridItemHTMLElement } from "@homarr/gridstack"; import type { GridItemHTMLElement } from "@homarr/gridstack";
import { WidgetSizeProvider } from "@homarr/widgets";
import type { DynamicSectionItem, SectionItem } from "~/app/[locale]/boards/_types"; import type { DynamicSectionItem, SectionItem } from "~/app/[locale]/boards/_types";
import { BoardItemContent } from "../items/item-content"; import { BoardItemContent } from "../items/item-content";
@@ -18,7 +19,7 @@ export const SectionContent = () => {
* @see https://github.com/homarr-labs/homarr/pull/1770 * @see https://github.com/homarr-labs/homarr/pull/1770
*/ */
const sortedItems = useMemo(() => { const sortedItems = useMemo(() => {
return [...items, ...innerSections].sort((itemA, itemB) => { return [...items, ...innerSections].toSorted((itemA, itemB) => {
if (itemA.yOffset === itemB.yOffset) { if (itemA.yOffset === itemB.yOffset) {
return itemA.xOffset - itemB.xOffset; return itemA.xOffset - itemB.xOffset;
} }
@@ -59,7 +60,13 @@ const Item = ({ item, innerRef }: ItemProps) => {
minWidth={minWidth} minWidth={minWidth}
minHeight={minHeight} minHeight={minHeight}
> >
{item.type === "item" ? <BoardItemContent item={item} /> : <BoardDynamicSection section={item} />} {item.type === "item" ? (
<WidgetSizeProvider gridWidth={item.width} gridHeight={item.height}>
<BoardItemContent item={item} />
</WidgetSizeProvider>
) : (
<BoardDynamicSection section={item} />
)}
</GridStackItem> </GridStackItem>
); );
}; };
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "homarr", "name": "homarr",
"version": "1.63.0", "version": "1.64.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "cross-env CI=true turbo build", "build": "cross-env CI=true turbo build",
@@ -88,13 +88,13 @@
"nanoid@>=4.0.0 <5.0.9": ">=5.1.11", "nanoid@>=4.0.0 <5.0.9": ">=5.1.11",
"prismjs@<1.30.0": ">=1.30.0", "prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.24.6", "proxmox-api>undici": "7.24.6",
"react-is": "^19.2.6", "react-is": "^19.2.7",
"rollup@>=4.0.0 <4.22.4": ">=4.60.4", "rollup@>=4.0.0 <4.22.4": ">=4.61.0",
"sha.js@<=2.4.11": ">=2.4.12", "sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2", "tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2", "tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
"tmp@<=0.2.3": ">=0.2.7", "tmp@<=0.2.3": ">=0.2.7",
"vite@>=5.0.0 <=5.4.18": ">=8.0.14" "vite@>=5.0.0 <=5.4.18": ">=8.0.16"
}, },
"patchedDependencies": { "patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch", "@types/node-unifi": "patches/@types__node-unifi.patch",
+1
View File
@@ -53,6 +53,7 @@ import * as weather from "./weather";
export type { WidgetDefinition, WidgetOptionsSettings } from "./definition"; export type { WidgetDefinition, WidgetOptionsSettings } from "./definition";
export type { WidgetComponentProps }; export type { WidgetComponentProps };
export { useWidgetSize, WidgetSizeProvider } from "./widget-size";
export const widgetImports = { export const widgetImports = {
clock, clock,
+25 -16
View File
@@ -11,6 +11,7 @@ import { metricToImperial } from "@homarr/common";
import { useScopedI18n } from "@homarr/translation/client"; import { useScopedI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition"; import type { WidgetComponentProps } from "../definition";
import { useWidgetSize } from "../widget-size";
import { WeatherDescription, WeatherIcon } from "./icon"; import { WeatherDescription, WeatherIcon } from "./icon";
export default function WeatherWidget({ isEditMode, options }: WidgetComponentProps<"weather">) { export default function WeatherWidget({ isEditMode, options }: WidgetComponentProps<"weather">) {
@@ -29,19 +30,23 @@ export default function WeatherWidget({ isEditMode, options }: WidgetComponentPr
onData: (data) => utils.widget.weather.atLocation.setData(input, data), onData: (data) => utils.widget.weather.atLocation.setData(input, data),
}); });
const { gridWidth, gridHeight } = useWidgetSize();
const isCompact = gridWidth <= 1 || gridHeight <= 1;
const showForecast = options.hasForecast && !isCompact;
return ( return (
<Stack <Stack
align="center" align="center"
gap="sm" gap={isCompact ? "xs" : "sm"}
justify="center" justify="center"
w="100%" w="100%"
h="100%" h="100%"
style={{ pointerEvents: isEditMode ? "none" : undefined }} style={{ pointerEvents: isEditMode ? "none" : undefined }}
> >
{options.hasForecast ? ( {showForecast ? (
<WeeklyForecast weather={weather} options={options} /> <WeeklyForecast weather={weather} options={options} />
) : ( ) : (
<DailyWeather weather={weather} options={options} /> <DailyWeather weather={weather} options={options} compact={isCompact} />
)} )}
</Stack> </Stack>
); );
@@ -51,24 +56,28 @@ interface WeatherProps extends Pick<WidgetComponentProps<"weather">, "options">
weather: RouterOutputs["widget"]["weather"]["atLocation"]; weather: RouterOutputs["widget"]["weather"]["atLocation"];
} }
const DailyWeather = ({ options, weather }: WeatherProps) => { const DailyWeather = ({ options, weather, compact }: WeatherProps & { compact?: boolean }) => {
const t = useScopedI18n("widget.weather"); const t = useScopedI18n("widget.weather");
const tCommon = useScopedI18n("common"); const tCommon = useScopedI18n("common");
const iconSize = compact ? 20 : 30;
const tempSize = compact ? 20 : 30;
const detailSize = compact ? 13 : 16;
return ( return (
<> <>
<Group className="weather-day-group" gap="sm"> <Group className="weather-day-group" gap="sm">
<HoverCard> <HoverCard>
<HoverCard.Target> <HoverCard.Target>
<Box> <Box>
<WeatherIcon size={30} code={weather.current.weathercode} /> <WeatherIcon size={iconSize} code={weather.current.weathercode} />
</Box> </Box>
</HoverCard.Target> </HoverCard.Target>
<HoverCard.Dropdown> <HoverCard.Dropdown>
<WeatherDescription weatherOnly weatherCode={weather.current.weathercode} /> <WeatherDescription weatherOnly weatherCode={weather.current.weathercode} />
</HoverCard.Dropdown> </HoverCard.Dropdown>
</HoverCard> </HoverCard>
<Text fz={30}> <Text fz={tempSize}>
{getPreferredUnit( {getPreferredUnit(
weather.current.temperature, weather.current.temperature,
options.isFormatFahrenheit, options.isFormatFahrenheit,
@@ -77,10 +86,10 @@ const DailyWeather = ({ options, weather }: WeatherProps) => {
</Text> </Text>
</Group> </Group>
<Stack gap="xs" align="center"> <Stack gap="xs" align="center">
{options.showCurrentWindSpeed && ( {options.showCurrentWindSpeed && !compact && (
<Group className="weather-current-wind-speed-group" wrap="nowrap" gap="xs"> <Group className="weather-current-wind-speed-group" wrap="nowrap" gap="xs">
<IconWind size={16} /> <IconWind size={detailSize} />
<Text fz={16}> <Text fz={detailSize}>
{t("currentWindSpeed", { {t("currentWindSpeed", {
currentWindSpeed: (options.useImperialSpeed currentWindSpeed: (options.useImperialSpeed
? metricToImperial(weather.current.windspeed) ? metricToImperial(weather.current.windspeed)
@@ -95,8 +104,8 @@ const DailyWeather = ({ options, weather }: WeatherProps) => {
)} )}
<Group className="weather-max-min-temp-group" wrap="nowrap" gap="sm"> <Group className="weather-max-min-temp-group" wrap="nowrap" gap="sm">
<Group gap="xs" wrap="nowrap"> <Group gap="xs" wrap="nowrap">
<IconArrowUpRight size={16} /> <IconArrowUpRight size={detailSize} />
<Text fz={16}> <Text fz={detailSize}>
{getPreferredUnit( {getPreferredUnit(
weather.daily[0]?.maxTemp, weather.daily[0]?.maxTemp,
options.isFormatFahrenheit, options.isFormatFahrenheit,
@@ -105,8 +114,8 @@ const DailyWeather = ({ options, weather }: WeatherProps) => {
</Text> </Text>
</Group> </Group>
<Group gap="xs" wrap="nowrap"> <Group gap="xs" wrap="nowrap">
<IconArrowDownRight size={16} /> <IconArrowDownRight size={detailSize} />
<Text fz={16}> <Text fz={detailSize}>
{getPreferredUnit( {getPreferredUnit(
weather.daily[0]?.minTemp, weather.daily[0]?.minTemp,
options.isFormatFahrenheit, options.isFormatFahrenheit,
@@ -116,11 +125,11 @@ const DailyWeather = ({ options, weather }: WeatherProps) => {
</Group> </Group>
</Group> </Group>
</Stack> </Stack>
{options.showCity && ( {options.showCity && !compact && (
<> <>
<Group className="weather-city-group" wrap="nowrap" gap="xs"> <Group className="weather-city-group" wrap="nowrap" gap="xs">
<IconMapPin size={16} /> <IconMapPin size={detailSize} />
<Text fz={16} style={{ whiteSpace: "nowrap" }}> <Text fz={detailSize} style={{ whiteSpace: "nowrap" }}>
{options.location.name} {options.location.name}
</Text> </Text>
</Group> </Group>
+25
View File
@@ -0,0 +1,25 @@
"use client";
import type { ReactNode } from "react";
import { createContext, useContext } from "react";
export interface WidgetSize {
gridWidth: number;
gridHeight: number;
}
const WidgetSizeContext = createContext<WidgetSize | null>(null);
export const useWidgetSize = () => {
const ctx = useContext(WidgetSizeContext);
if (!ctx) throw new Error("useWidgetSize must be used within WidgetSizeProvider");
return ctx;
};
interface WidgetSizeProviderProps extends WidgetSize {
children: ReactNode;
}
export const WidgetSizeProvider = ({ gridWidth, gridHeight, children }: WidgetSizeProviderProps) => (
<WidgetSizeContext.Provider value={{ gridWidth, gridHeight }}>{children}</WidgetSizeContext.Provider>
);
+556 -572
View File
File diff suppressed because it is too large. Load diff
+10 -10
View File
@@ -14,7 +14,7 @@ catalog:
"@dnd-kit/utilities": ^3.2.2 "@dnd-kit/utilities": ^3.2.2
"@drizzle-team/brocli": ^0.12.0 "@drizzle-team/brocli": ^0.12.0
"@extractus/feed-extractor": 7.2.1 "@extractus/feed-extractor": 7.2.1
'@gfazioli/mantine-onboarding-tour': ^4.0.1 '@gfazioli/mantine-onboarding-tour': ^4.0.3
"@gitbeaker/rest": ^43.8.0 "@gitbeaker/rest": ^43.8.0
"@homarr/gridstack": ^1.12.0 "@homarr/gridstack": ^1.12.0
"@homarr/node-unifi": ^2.6.0 "@homarr/node-unifi": ^2.6.0
@@ -32,7 +32,7 @@ catalog:
"@mantine/notifications": ^9.2.2 "@mantine/notifications": ^9.2.2
"@mantine/spotlight": ^9.2.2 "@mantine/spotlight": ^9.2.2
"@mantine/tiptap": ^9.2.2 "@mantine/tiptap": ^9.2.2
"@next/eslint-plugin-next": 16.2.6 "@next/eslint-plugin-next": 16.2.7
"@octokit/auth-app": ^8.2.0 "@octokit/auth-app": ^8.2.0
"@paralleldrive/cuid2": ^3.3.0 "@paralleldrive/cuid2": ^3.3.0
"@playwright/test": ^1.60.0 "@playwright/test": ^1.60.0
@@ -84,7 +84,7 @@ catalog:
"@types/node-unifi": ^2.5.1 "@types/node-unifi": ^2.5.1
"@types/pg": ^8.20.0 "@types/pg": ^8.20.0
"@types/prismjs": ^1.26.6 "@types/prismjs": ^1.26.6
"@types/react": 19.2.15 "@types/react": 19.2.16
"@types/react-dom": 19.2.3 "@types/react-dom": 19.2.3
"@types/swagger-ui-react": ^5.18.0 "@types/swagger-ui-react": ^5.18.0
"@types/video.js": ^7.3.58 "@types/video.js": ^7.3.58
@@ -92,8 +92,8 @@ catalog:
"@types/xml2js": ^0.4.14 "@types/xml2js": ^0.4.14
"@umami/node": ^0.4.0 "@umami/node": ^0.4.0
"@vitejs/plugin-react": ^5.2.0 "@vitejs/plugin-react": ^5.2.0
"@vitest/coverage-v8": ^4.1.7 "@vitest/coverage-v8": ^4.1.8
"@vitest/ui": ^4.1.7 "@vitest/ui": ^4.1.8
"@xterm/addon-canvas": ^0.7.0 "@xterm/addon-canvas": ^0.7.0
"@xterm/addon-fit": 0.11.0 "@xterm/addon-fit": 0.11.0
"@xterm/xterm": ^6.0.0 "@xterm/xterm": ^6.0.0
@@ -135,19 +135,19 @@ catalog:
mantine-react-table: npm:mantine-react-table-open@9.0.3 mantine-react-table: npm:mantine-react-table-open@9.0.3
maria2: ^0.4.1 maria2: ^0.4.1
mysql2: 3.22.4 mysql2: 3.22.4
next: 16.2.6 next: 16.2.7
next-auth: 5.0.0-beta.31 next-auth: 5.0.0-beta.31
next-intl: 4.13.0 next-intl: 4.13.0
node-loader: ^2.1.0 node-loader: ^2.1.0
octokit: ^5.0.5 octokit: ^5.0.5
oxfmt: ^0.52.0 oxfmt: ^0.53.0
oxlint: ^1.67.0 oxlint: ^1.67.0
pg: ^8.21.0 pg: ^8.21.0
postcss-preset-mantine: ^1.18.0 postcss-preset-mantine: ^1.18.0
prismjs: ^1.30.0 prismjs: ^1.30.0
proxmox-api: 1.1.1 proxmox-api: 1.1.1
react: 19.2.6 react: 19.2.7
react-dom: 19.2.6 react-dom: 19.2.7
react-error-boundary: ^6.1.2 react-error-boundary: ^6.1.2
react-markdown: ^10.1.0 react-markdown: ^10.1.0
react-quill-new: ^3.8.3 react-quill-new: ^3.8.3
@@ -170,7 +170,7 @@ catalog:
use-deep-compare-effect: ^1.8.1 use-deep-compare-effect: ^1.8.1
video.js: ^8.23.7 video.js: ^8.23.7
vite-tsconfig-paths: ^6.1.1 vite-tsconfig-paths: ^6.1.1
vitest: ^4.1.7 vitest: ^4.1.8
winston: 3.19.0 winston: 3.19.0
ws: ^8.21.0 ws: ^8.21.0
xml2js: ^0.6.2 xml2js: ^0.6.2