diff --git a/.nvmrc b/.nvmrc
index 6fa8dec4c..d5b283a3a 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-22.13.0
+22.13.1
diff --git a/apps/nextjs/next.config.ts b/apps/nextjs/next.config.ts
index 5a082309c..53148f725 100644
--- a/apps/nextjs/next.config.ts
+++ b/apps/nextjs/next.config.ts
@@ -2,6 +2,7 @@
import "@homarr/auth/env";
import "@homarr/db/env";
import "@homarr/common/env";
+import "@homarr/docker/env";
import type { NextConfig } from "next";
import MillionLint from "@million/lint";
diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 81ec38d17..4ac217d27 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -23,8 +23,10 @@
"@homarr/cron-job-status": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
+ "@homarr/docker": "workspace:^0.1.0",
"@homarr/form": "workspace:^0.1.0",
- "@homarr/gridstack": "^1.11.3",
+ "@homarr/gridstack": "^1.12.0",
+ "@homarr/icons": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/log": "workspace:^",
"@homarr/modals": "workspace:^0.1.0",
@@ -39,18 +41,18 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
- "@mantine/colors-generator": "^7.16.0",
- "@mantine/core": "^7.16.0",
- "@mantine/dropzone": "^7.16.0",
- "@mantine/hooks": "^7.16.0",
- "@mantine/modals": "^7.16.0",
- "@mantine/tiptap": "^7.16.0",
+ "@mantine/colors-generator": "^7.16.1",
+ "@mantine/core": "^7.16.1",
+ "@mantine/dropzone": "^7.16.1",
+ "@mantine/hooks": "^7.16.1",
+ "@mantine/modals": "^7.16.1",
+ "@mantine/tiptap": "^7.16.1",
"@million/lint": "1.0.14",
"@t3-oss/env-nextjs": "^0.11.1",
- "@tabler/icons-react": "^3.28.1",
- "@tanstack/react-query": "^5.64.1",
- "@tanstack/react-query-devtools": "^5.64.1",
- "@tanstack/react-query-next-experimental": "5.64.1",
+ "@tabler/icons-react": "^3.29.0",
+ "@tanstack/react-query": "^5.64.2",
+ "@tanstack/react-query-devtools": "^5.64.2",
+ "@tanstack/react-query-next-experimental": "5.64.2",
"@trpc/client": "next",
"@trpc/next": "next",
"@trpc/react-query": "next",
@@ -64,9 +66,9 @@
"dotenv": "^16.4.7",
"flag-icons": "^7.3.2",
"glob": "^11.0.1",
- "jotai": "^2.11.0",
+ "jotai": "^2.11.1",
"mantine-react-table": "2.0.0-beta.8",
- "next": "15.1.5",
+ "next": "15.1.6",
"postcss-preset-mantine": "^1.17.0",
"prismjs": "^1.29.0",
"react": "19.0.0",
diff --git a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx
index 3d2042030..2588cf44a 100644
--- a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx
+++ b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx
@@ -94,6 +94,7 @@ export function TRPCReactProvider(props: PropsWithChildren) {
false: unstable_httpBatchStreamLink({
transformer: superjson,
url: getTrpcUrl(),
+ maxURLLength: 2083, // Suggested by tRPC: https://trpc.io/docs/client/links/httpBatchLink#setting-a-maximum-url-length
headers: createHeadersCallbackForSource("nextjs-react (json)"),
}),
}),
diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx
index b0657e1e1..74c617d09 100644
--- a/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx
@@ -2,6 +2,7 @@
import type { MouseEvent } from "react";
import { useCallback, useEffect } from "react";
+import Link from "next/link";
import { useRouter } from "next/navigation";
import { Group, Menu } from "@mantine/core";
import { useHotkeys } from "@mantine/hooks";
@@ -9,9 +10,11 @@ import {
IconBox,
IconBoxAlignTop,
IconChevronDown,
+ IconLayoutBoard,
IconPencil,
IconPencilOff,
IconPlus,
+ IconReplace,
IconResize,
IconSettings,
} from "@tabler/icons-react";
@@ -49,6 +52,8 @@ export const BoardContentHeaderActions = () => {
+
+
>
);
};
@@ -151,6 +156,32 @@ const EditModeMenu = () => {
);
};
+const SelectBoardsMenu = () => {
+ const { data: boards = [] } = clientApi.board.getAllBoards.useQuery();
+
+ return (
+
+ );
+};
+
const usePreventLeaveWithDirty = (isDirty: boolean) => {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
index 188916e9e..bc5746292 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
@@ -15,7 +15,9 @@ export const IntegrationCreateDropdownContent = () => {
const [search, setSearch] = useState("");
const filteredKinds = useMemo(() => {
- return integrationKinds.filter((kind) => kind.includes(search.toLowerCase()));
+ return integrationKinds.filter((kind) =>
+ getIntegrationName(kind).toLowerCase().includes(search.toLowerCase().trim()),
+ );
}, [search]);
const handleSearch = React.useCallback(
@@ -29,6 +31,7 @@ export const IntegrationCreateDropdownContent = () => {
leftSection={}
placeholder={t("integration.page.list.search")}
value={search}
+ data-autofocus
onChange={handleSearch}
/>
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
index 38a0a2cdf..2daeea657 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
@@ -102,7 +102,15 @@ export default async function IntegrationsPage(props: IntegrationsPageProps) {
const IntegrationSelectMenu = ({ children }: PropsWithChildren) => {
return (
-