diff --git a/ui/package-lock.json b/ui/package-lock.json
index 9304cec..13c6e99 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "kvm-ui",
- "version": "2025.09.03.1500",
+ "version": "2025.09.03.2100",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kvm-ui",
- "version": "2025.09.03.1500",
+ "version": "2025.09.03.2100",
"dependencies": {
"@headlessui/react": "^2.2.7",
"@headlessui/tailwindcss": "^0.2.2",
@@ -30,7 +30,7 @@
"react-dom": "^19.1.1",
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
- "react-router-dom": "^6.22.3",
+ "react-router": "^7.8.2",
"react-simple-keyboard": "^3.8.119",
"react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10",
@@ -54,7 +54,7 @@
"@types/validator": "^13.15.3",
"@typescript-eslint/eslint-plugin": "^8.42.0",
"@typescript-eslint/parser": "^8.42.0",
- "@vitejs/plugin-react-swc": "^3.10.2",
+ "@vitejs/plugin-react-swc": "^4.0.1",
"autoprefixer": "^10.4.21",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
@@ -68,7 +68,7 @@
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
- "vite": "^6.3.5",
+ "vite": "^7.1.4",
"vite-tsconfig-paths": "^5.1.4"
},
"engines": {
@@ -1019,19 +1019,10 @@
}
}
},
- "node_modules/@remix-run/router": {
- "version": "1.23.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
- "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.27",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
- "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz",
+ "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==",
"dev": true,
"license": "MIT"
},
@@ -2311,14 +2302,17 @@
}
},
"node_modules/@vitejs/plugin-react-swc": {
- "version": "3.11.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz",
- "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.0.1.tgz",
+ "integrity": "sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@rolldown/pluginutils": "1.0.0-beta.27",
- "@swc/core": "^1.12.11"
+ "@rolldown/pluginutils": "1.0.0-beta.32",
+ "@swc/core": "^1.13.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"vite": "^4 || ^5 || ^6 || ^7"
@@ -2850,6 +2844,15 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -5881,35 +5884,25 @@
}
},
"node_modules/react-router": {
- "version": "6.30.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz",
- "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==",
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz",
+ "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.23.0"
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
},
"engines": {
- "node": ">=14.0.0"
+ "node": ">=20.0.0"
},
"peerDependencies": {
- "react": ">=16.8"
- }
- },
- "node_modules/react-router-dom": {
- "version": "6.30.1",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
- "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
- "license": "MIT",
- "dependencies": {
- "@remix-run/router": "1.23.0",
- "react-router": "6.30.1"
+ "react": ">=18",
+ "react-dom": ">=18"
},
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8",
- "react-dom": ">=16.8"
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
}
},
"node_modules/react-simple-keyboard": {
@@ -6202,6 +6195,12 @@
"node": ">=10"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "license": "MIT"
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -6894,23 +6893,23 @@
}
},
"node_modules/vite": {
- "version": "6.3.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
- "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz",
+ "integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.14"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -6919,14 +6918,14 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
- "less": "*",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
diff --git a/ui/package.json b/ui/package.json
index 923bf57..4c98825 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,7 +1,7 @@
{
"name": "kvm-ui",
"private": true,
- "version": "2025.09.03.1500",
+ "version": "2025.09.03.2100",
"type": "module",
"engines": {
"node": "22.15.0"
@@ -41,7 +41,7 @@
"react-dom": "^19.1.1",
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
- "react-router-dom": "^6.22.3",
+ "react-router": "^7.8.2",
"react-simple-keyboard": "^3.8.119",
"react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10",
@@ -65,7 +65,7 @@
"@types/validator": "^13.15.3",
"@typescript-eslint/eslint-plugin": "^8.42.0",
"@typescript-eslint/parser": "^8.42.0",
- "@vitejs/plugin-react-swc": "^3.10.2",
+ "@vitejs/plugin-react-swc": "^4.0.1",
"autoprefixer": "^10.4.21",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
@@ -79,7 +79,7 @@
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2",
- "vite": "^6.3.5",
+ "vite": "^7.1.4",
"vite-tsconfig-paths": "^5.1.4"
}
}
diff --git a/ui/src/components/AuthLayout.tsx b/ui/src/components/AuthLayout.tsx
index 6c6d5da..7d66e95 100644
--- a/ui/src/components/AuthLayout.tsx
+++ b/ui/src/components/AuthLayout.tsx
@@ -1,4 +1,4 @@
-import { useLocation, useNavigation, useSearchParams } from "react-router-dom";
+import { useLocation, useNavigation, useSearchParams } from "react-router";
import { Button, LinkButton } from "@components/Button";
import { GoogleIcon } from "@components/Icons";
diff --git a/ui/src/components/Button.tsx b/ui/src/components/Button.tsx
index b7f0950..b1dc3ab 100644
--- a/ui/src/components/Button.tsx
+++ b/ui/src/components/Button.tsx
@@ -1,5 +1,6 @@
import React, { JSX } from "react";
-import { FetcherWithComponents, Link, LinkProps, useNavigation } from "react-router-dom";
+import { Link, useNavigation } from "react-router";
+import type { FetcherWithComponents, LinkProps } from "react-router";
import ExtLink from "@/components/ExtLink";
import LoadingSpinner from "@/components/LoadingSpinner";
diff --git a/ui/src/components/Fieldset.tsx b/ui/src/components/Fieldset.tsx
index 9a37e79..06b7ab9 100644
--- a/ui/src/components/Fieldset.tsx
+++ b/ui/src/components/Fieldset.tsx
@@ -1,6 +1,7 @@
import React from "react";
import clsx from "clsx";
-import { FetcherWithComponents, useNavigation } from "react-router-dom";
+import { useNavigation } from "react-router";
+import type { FetcherWithComponents } from "react-router";
export default function Fieldset({
children,
diff --git a/ui/src/components/Header.tsx b/ui/src/components/Header.tsx
index 4bb7a97..a290114 100644
--- a/ui/src/components/Header.tsx
+++ b/ui/src/components/Header.tsx
@@ -1,5 +1,5 @@
import { useCallback } from "react";
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
import { ArrowLeftEndOnRectangleIcon, ChevronDownIcon } from "@heroicons/react/16/solid";
import { Button, Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { LuMonitorSmartphone } from "react-icons/lu";
diff --git a/ui/src/components/KvmCard.tsx b/ui/src/components/KvmCard.tsx
index 91296c5..7ef8712 100644
--- a/ui/src/components/KvmCard.tsx
+++ b/ui/src/components/KvmCard.tsx
@@ -1,6 +1,6 @@
import { MdConnectWithoutContact } from "react-icons/md";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
import { LuEllipsisVertical } from "react-icons/lu";
import Card from "@components/Card";
diff --git a/ui/src/components/SimpleNavbar.tsx b/ui/src/components/SimpleNavbar.tsx
index 7652ad0..1e1c19e 100644
--- a/ui/src/components/SimpleNavbar.tsx
+++ b/ui/src/components/SimpleNavbar.tsx
@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link } from "react-router";
import React from "react";
import Container from "@/components/Container";
diff --git a/ui/src/components/popovers/MountPopover.tsx b/ui/src/components/popovers/MountPopover.tsx
index 1381293..8b6a8a5 100644
--- a/ui/src/components/popovers/MountPopover.tsx
+++ b/ui/src/components/popovers/MountPopover.tsx
@@ -6,7 +6,7 @@ import {
LuRadioReceiver,
} from "react-icons/lu";
import { useClose } from "@headlessui/react";
-import { useLocation } from "react-router-dom";
+import { useLocation } from "react-router";
import { Button } from "@components/Button";
import Card, { GridCard } from "@components/Card";
diff --git a/ui/src/hooks/useAppNavigation.ts b/ui/src/hooks/useAppNavigation.ts
index 6c9270a..af9a247 100644
--- a/ui/src/hooks/useAppNavigation.ts
+++ b/ui/src/hooks/useAppNavigation.ts
@@ -1,4 +1,5 @@
-import { useNavigate, useParams, NavigateOptions } from "react-router-dom";
+import { useNavigate, useParams } from "react-router";
+import type { NavigateOptions } from "react-router";
import { useCallback, useMemo } from "react";
import { isOnDevice } from "../main";
diff --git a/ui/src/main.tsx b/ui/src/main.tsx
index 5374658..87b3b18 100644
--- a/ui/src/main.tsx
+++ b/ui/src/main.tsx
@@ -7,7 +7,7 @@ import {
redirect,
RouterProvider,
useRouteError,
-} from "react-router-dom";
+} from "react-router";
import { ExclamationTriangleIcon } from "@heroicons/react/16/solid";
import { CLOUD_API, DEVICE_API } from "@/ui.config";
@@ -28,7 +28,7 @@ import DeviceIdRename from "@routes/devices.$id.rename";
import DevicesRoute from "@routes/devices";
import SettingsIndexRoute from "@routes/devices.$id.settings._index";
import SettingsAccessIndexRoute from "@routes/devices.$id.settings.access._index";
-const Notifications = lazy(() => import("@/notifications"));
+import Notifications from "@/notifications";
const SignupRoute = lazy(() => import("@routes/signup"));
const LoginRoute = lazy(() => import("@routes/login"));
const DevicesAlreadyAdopted = lazy(() => import("@routes/devices.already-adopted"));
diff --git a/ui/src/root.tsx b/ui/src/root.tsx
index 9413055..8995fd9 100644
--- a/ui/src/root.tsx
+++ b/ui/src/root.tsx
@@ -1,4 +1,4 @@
-import { Outlet } from "react-router-dom";
+import { Outlet } from "react-router";
function Root() {
return ;
diff --git a/ui/src/routes/adopt.tsx b/ui/src/routes/adopt.tsx
index 8b8325b..b7079f5 100644
--- a/ui/src/routes/adopt.tsx
+++ b/ui/src/routes/adopt.tsx
@@ -1,8 +1,8 @@
-import { LoaderFunctionArgs, redirect } from "react-router-dom";
+import { redirect } from "react-router";
+import type { LoaderFunction, LoaderFunctionArgs } from "react-router";
import { DEVICE_API } from "@/ui.config";
-
-import api from "../api";
+import api from "@/api";
export interface CloudState {
connected: boolean;
@@ -10,7 +10,7 @@ export interface CloudState {
appUrl: string;
}
-const loader = async ({ request }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const searchParams = url.searchParams;
@@ -37,7 +37,7 @@ const loader = async ({ request }: LoaderFunctionArgs) => {
};
export default function AdoptRoute() {
- return <>>;
+ return (<>>);
}
AdoptRoute.loader = loader;
diff --git a/ui/src/routes/devices.$id.deregister.tsx b/ui/src/routes/devices.$id.deregister.tsx
index 8c0a87f..e5dd2a3 100644
--- a/ui/src/routes/devices.$id.deregister.tsx
+++ b/ui/src/routes/devices.$id.deregister.tsx
@@ -1,11 +1,5 @@
-import {
- ActionFunctionArgs,
- Form,
- LoaderFunctionArgs,
- redirect,
- useActionData,
- useLoaderData,
-} from "react-router-dom";
+import { Form, redirect, useActionData, useLoaderData } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router";
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
import { Button, LinkButton } from "@components/Button";
@@ -22,7 +16,7 @@ interface LoaderData {
user: User;
}
-const action = async ({ request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
const { deviceId } = Object.fromEntries(await request.formData());
try {
@@ -34,17 +28,17 @@ const action = async ({ request }: ActionFunctionArgs) => {
});
if (!res.ok) {
- return { message: "There was an error renaming your device. Please try again." };
+ return { message: "There was an error deregistering your device. Please try again." };
}
} catch (e) {
console.error(e);
- return { message: "There was an error renaming your device. Please try again." };
+ return { message: "There was an error deregistering your device. Please try again." };
}
return redirect("/devices");
};
-const loader = async ({ params }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
const user = await checkAuth();
const { id } = params;
diff --git a/ui/src/routes/devices.$id.mount.tsx b/ui/src/routes/devices.$id.mount.tsx
index 68979be..c62308d 100644
--- a/ui/src/routes/devices.$id.mount.tsx
+++ b/ui/src/routes/devices.$id.mount.tsx
@@ -7,7 +7,7 @@ import {
} from "react-icons/lu";
import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid";
import { TrashIcon } from "@heroicons/react/16/solid";
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
import Card, { GridCard } from "@/components/Card";
import { Button } from "@components/Button";
diff --git a/ui/src/routes/devices.$id.other-session.tsx b/ui/src/routes/devices.$id.other-session.tsx
index 16cb479..8a767d5 100644
--- a/ui/src/routes/devices.$id.other-session.tsx
+++ b/ui/src/routes/devices.$id.other-session.tsx
@@ -1,4 +1,4 @@
-import { useNavigate, useOutletContext } from "react-router-dom";
+import { useNavigate, useOutletContext } from "react-router";
import { GridCard } from "@/components/Card";
import { Button } from "@components/Button";
diff --git a/ui/src/routes/devices.$id.rename.tsx b/ui/src/routes/devices.$id.rename.tsx
index 2852561..39f06bc 100644
--- a/ui/src/routes/devices.$id.rename.tsx
+++ b/ui/src/routes/devices.$id.rename.tsx
@@ -1,11 +1,5 @@
-import {
- ActionFunctionArgs,
- Form,
- LoaderFunctionArgs,
- redirect,
- useActionData,
- useLoaderData,
-} from "react-router-dom";
+import { Form, redirect, useActionData, useLoaderData } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router";
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
import { Button, LinkButton } from "@components/Button";
@@ -25,7 +19,7 @@ interface LoaderData {
user: User;
}
-const action = async ({ params, request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ params, request }: ActionFunctionArgs) => {
const { id } = params;
const { name } = Object.fromEntries(await request.formData());
@@ -48,7 +42,7 @@ const action = async ({ params, request }: ActionFunctionArgs) => {
return redirect("/devices");
};
-const loader = async ({ params }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
const user = await checkAuth();
const { id } = params;
diff --git a/ui/src/routes/devices.$id.settings._index.tsx b/ui/src/routes/devices.$id.settings._index.tsx
index 4fb35ed..e4a9d7f 100644
--- a/ui/src/routes/devices.$id.settings._index.tsx
+++ b/ui/src/routes/devices.$id.settings._index.tsx
@@ -1,8 +1,9 @@
-import { LoaderFunctionArgs, redirect } from "react-router-dom";
+import { redirect } from "react-router";
+import type { LoaderFunction, LoaderFunctionArgs } from "react-router";
import { getDeviceUiPath } from "../hooks/useAppNavigation";
-const loader = ({ params }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
return redirect(getDeviceUiPath("/settings/general", params.id));
}
diff --git a/ui/src/routes/devices.$id.settings.access._index.tsx b/ui/src/routes/devices.$id.settings.access._index.tsx
index 31b7bb7..b5ccca0 100644
--- a/ui/src/routes/devices.$id.settings.access._index.tsx
+++ b/ui/src/routes/devices.$id.settings.access._index.tsx
@@ -1,4 +1,5 @@
-import { useLoaderData, useNavigate } from "react-router-dom";
+import { useLoaderData, useNavigate } from "react-router";
+import type { LoaderFunction } from "react-router";
import { ShieldCheckIcon } from "@heroicons/react/24/outline";
import { useCallback, useEffect, useState } from "react";
@@ -26,7 +27,7 @@ export interface TLSState {
privateKey?: string;
}
-const loader = async () => {
+const loader: LoaderFunction = async () => {
if (isOnDevice) {
const status = await api
.GET(`${DEVICE_API}/device`)
diff --git a/ui/src/routes/devices.$id.settings.access.local-auth.tsx b/ui/src/routes/devices.$id.settings.access.local-auth.tsx
index 50b2cc4..5f5231d 100644
--- a/ui/src/routes/devices.$id.settings.access.local-auth.tsx
+++ b/ui/src/routes/devices.$id.settings.access.local-auth.tsx
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
-import { useLocation, useRevalidator } from "react-router-dom";
+import { useLocation, useRevalidator } from "react-router";
import { Button } from "@components/Button";
import { InputFieldWithLabel } from "@/components/InputField";
diff --git a/ui/src/routes/devices.$id.settings.general.reboot.tsx b/ui/src/routes/devices.$id.settings.general.reboot.tsx
index 0bf114c..db0e053 100644
--- a/ui/src/routes/devices.$id.settings.general.reboot.tsx
+++ b/ui/src/routes/devices.$id.settings.general.reboot.tsx
@@ -1,4 +1,4 @@
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
import { useCallback } from "react";
import { useJsonRpc } from "@/hooks/useJsonRpc";
diff --git a/ui/src/routes/devices.$id.settings.general.update.tsx b/ui/src/routes/devices.$id.settings.general.update.tsx
index b719d7e..80ba0f7 100644
--- a/ui/src/routes/devices.$id.settings.general.update.tsx
+++ b/ui/src/routes/devices.$id.settings.general.update.tsx
@@ -1,4 +1,4 @@
-import { useLocation, useNavigate } from "react-router-dom";
+import { useLocation, useNavigate } from "react-router";
import { useCallback, useEffect, useRef, useState } from "react";
import { CheckCircleIcon } from "@heroicons/react/20/solid";
diff --git a/ui/src/routes/devices.$id.settings.macros.add.tsx b/ui/src/routes/devices.$id.settings.macros.add.tsx
index 1b3ce30..7a9f493 100644
--- a/ui/src/routes/devices.$id.settings.macros.add.tsx
+++ b/ui/src/routes/devices.$id.settings.macros.add.tsx
@@ -1,4 +1,4 @@
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
import { useState } from "react";
import { KeySequence, useMacrosStore, generateMacroId } from "@/hooks/stores";
diff --git a/ui/src/routes/devices.$id.settings.macros.edit.tsx b/ui/src/routes/devices.$id.settings.macros.edit.tsx
index 336fe85..131fa1c 100644
--- a/ui/src/routes/devices.$id.settings.macros.edit.tsx
+++ b/ui/src/routes/devices.$id.settings.macros.edit.tsx
@@ -1,4 +1,4 @@
-import { useNavigate, useParams } from "react-router-dom";
+import { useNavigate, useParams } from "react-router";
import { useState, useEffect } from "react";
import { LuTrash2 } from "react-icons/lu";
diff --git a/ui/src/routes/devices.$id.settings.macros.tsx b/ui/src/routes/devices.$id.settings.macros.tsx
index 734f17e..94fded3 100644
--- a/ui/src/routes/devices.$id.settings.macros.tsx
+++ b/ui/src/routes/devices.$id.settings.macros.tsx
@@ -1,5 +1,5 @@
import { useEffect, Fragment, useMemo, useState, useCallback } from "react";
-import { useNavigate } from "react-router-dom";
+import { useNavigate } from "react-router";
import {
LuPenLine,
LuCopy,
diff --git a/ui/src/routes/devices.$id.settings.tsx b/ui/src/routes/devices.$id.settings.tsx
index 5a61778..49f2636 100644
--- a/ui/src/routes/devices.$id.settings.tsx
+++ b/ui/src/routes/devices.$id.settings.tsx
@@ -1,4 +1,4 @@
-import { NavLink, Outlet, useLocation } from "react-router-dom";
+import { NavLink, Outlet, useLocation } from "react-router";
import {
LuSettings,
LuMouse,
diff --git a/ui/src/routes/devices.$id.setup.tsx b/ui/src/routes/devices.$id.setup.tsx
index 1c477d6..2fd65f5 100644
--- a/ui/src/routes/devices.$id.setup.tsx
+++ b/ui/src/routes/devices.$id.setup.tsx
@@ -1,12 +1,5 @@
-import {
- ActionFunctionArgs,
- Form,
- LoaderFunctionArgs,
- redirect,
- useActionData,
- useParams,
- useSearchParams,
-} from "react-router-dom";
+import { Form, redirect, useActionData, useParams, useSearchParams } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router";
import SimpleNavbar from "@components/SimpleNavbar";
import GridBackground from "@components/GridBackground";
@@ -20,7 +13,7 @@ import { CLOUD_API } from "@/ui.config";
import api from "../api";
-const loader = async ({ params }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
await checkAuth();
const res = await fetch(`${CLOUD_API}/devices/${params.id}`, {
method: "GET",
@@ -35,7 +28,7 @@ const loader = async ({ params }: LoaderFunctionArgs) => {
}
};
-const action = async ({ request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
// Handle form submission
const { name, id, returnTo } = Object.fromEntries(await request.formData());
const res = await api.PUT(`${CLOUD_API}/devices/${id}`, { name });
@@ -43,7 +36,7 @@ const action = async ({ request }: ActionFunctionArgs) => {
if (res.ok) {
return redirect(returnTo?.toString() ?? `/devices/${id}`);
} else {
- return { error: "There was an error creating your device" };
+ return { error: "There was an error registering your device" };
}
};
diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx
index 9be05f6..a60c39a 100644
--- a/ui/src/routes/devices.$id.tsx
+++ b/ui/src/routes/devices.$id.tsx
@@ -1,8 +1,6 @@
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
- LoaderFunctionArgs,
Outlet,
- Params,
redirect,
useLoaderData,
useLocation,
@@ -10,7 +8,8 @@ import {
useOutlet,
useParams,
useSearchParams,
-} from "react-router-dom";
+} from "react-router";
+import type { LoaderFunction, LoaderFunctionArgs, Params } from "react-router";
import { useInterval } from "usehooks-ts";
import { FocusTrap } from "focus-trap-react";
import { motion, AnimatePresence } from "framer-motion";
@@ -112,7 +111,7 @@ const cloudLoader = async (params: Params): Promise =>
return { user, iceConfig, deviceName: device.name || device.id };
};
-const loader = ({ params }: LoaderFunctionArgs) => {
+const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
return import.meta.env.MODE === "device" ? deviceLoader() : cloudLoader(params);
};
diff --git a/ui/src/routes/devices.tsx b/ui/src/routes/devices.tsx
index b6af0f0..1dac696 100644
--- a/ui/src/routes/devices.tsx
+++ b/ui/src/routes/devices.tsx
@@ -1,4 +1,5 @@
-import { useLoaderData, useRevalidator } from "react-router-dom";
+import { useLoaderData, useRevalidator } from "react-router";
+import type { LoaderFunction } from "react-router";
import { LuMonitorSmartphone } from "react-icons/lu";
import { ArrowRightIcon } from "@heroicons/react/16/solid";
import { useInterval } from "usehooks-ts";
@@ -16,7 +17,7 @@ interface LoaderData {
user: User;
}
-const loader = async () => {
+const loader: LoaderFunction = async () => {
const user = await checkAuth();
try {
diff --git a/ui/src/routes/login-local.tsx b/ui/src/routes/login-local.tsx
index b2b189c..5fab7e6 100644
--- a/ui/src/routes/login-local.tsx
+++ b/ui/src/routes/login-local.tsx
@@ -1,4 +1,5 @@
-import { ActionFunctionArgs, Form, redirect, useActionData } from "react-router-dom";
+import { Form, redirect, useActionData } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router";
import { useState } from "react";
import { LuEye, LuEyeOff } from "react-icons/lu";
@@ -17,7 +18,7 @@ import ExtLink from "../components/ExtLink";
import { DeviceStatus } from "./welcome-local";
-const loader = async () => {
+const loader: LoaderFunction = async () => {
const res = await api
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise);
@@ -29,7 +30,7 @@ const loader = async () => {
return null;
};
-const action = async ({ request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const password = formData.get("password");
diff --git a/ui/src/routes/login.tsx b/ui/src/routes/login.tsx
index e2347a7..fb0f0c4 100644
--- a/ui/src/routes/login.tsx
+++ b/ui/src/routes/login.tsx
@@ -1,4 +1,4 @@
-import { useLocation, useSearchParams } from "react-router-dom";
+import { useLocation, useSearchParams } from "react-router";
import AuthLayout from "@components/AuthLayout";
diff --git a/ui/src/routes/signup.tsx b/ui/src/routes/signup.tsx
index af06400..c6efbcb 100644
--- a/ui/src/routes/signup.tsx
+++ b/ui/src/routes/signup.tsx
@@ -1,4 +1,4 @@
-import { useLocation, useSearchParams } from "react-router-dom";
+import { useLocation, useSearchParams } from "react-router";
import AuthLayout from "@components/AuthLayout";
diff --git a/ui/src/routes/welcome-local.mode.tsx b/ui/src/routes/welcome-local.mode.tsx
index 06ca62a..8d1a808 100644
--- a/ui/src/routes/welcome-local.mode.tsx
+++ b/ui/src/routes/welcome-local.mode.tsx
@@ -1,4 +1,5 @@
-import { ActionFunctionArgs, Form, redirect, useActionData } from "react-router-dom";
+import { Form, redirect, useActionData } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router";
import { useState } from "react";
import GridBackground from "@components/GridBackground";
@@ -14,7 +15,7 @@ import api from "../api";
import { DeviceStatus } from "./welcome-local";
-const loader = async () => {
+const loader: LoaderFunction = async () => {
const res = await api
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise);
@@ -23,7 +24,7 @@ const loader = async () => {
return null;
};
-const action = async ({ request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const localAuthMode = formData.get("localAuthMode");
if (!localAuthMode) return { error: "Please select an authentication mode" };
@@ -162,5 +163,5 @@ export default function WelcomeLocalModeRoute() {
);
}
-WelcomeLocalModeRoute.action = action;
WelcomeLocalModeRoute.loader = loader;
+WelcomeLocalModeRoute.action = action;
diff --git a/ui/src/routes/welcome-local.password.tsx b/ui/src/routes/welcome-local.password.tsx
index 4b2c05d..d0b7c7a 100644
--- a/ui/src/routes/welcome-local.password.tsx
+++ b/ui/src/routes/welcome-local.password.tsx
@@ -1,4 +1,5 @@
-import { ActionFunctionArgs, Form, redirect, useActionData } from "react-router-dom";
+import { Form, redirect, useActionData } from "react-router";
+import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router";
import { useState, useRef, useEffect } from "react";
import { LuEye, LuEyeOff } from "react-icons/lu";
@@ -15,7 +16,7 @@ import api from "../api";
import { DeviceStatus } from "./welcome-local";
-const loader = async () => {
+const loader: LoaderFunction = async () => {
const res = await api
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise);
@@ -24,7 +25,7 @@ const loader = async () => {
return null;
};
-const action = async ({ request }: ActionFunctionArgs) => {
+const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const password = formData.get("password");
const confirmPassword = formData.get("confirmPassword");
@@ -174,5 +175,5 @@ export default function WelcomeLocalPasswordRoute() {
);
}
-WelcomeLocalPasswordRoute.action = action;
WelcomeLocalPasswordRoute.loader = loader;
+WelcomeLocalPasswordRoute.action = action;
diff --git a/ui/src/routes/welcome-local.tsx b/ui/src/routes/welcome-local.tsx
index c516952..d7ff117 100644
--- a/ui/src/routes/welcome-local.tsx
+++ b/ui/src/routes/welcome-local.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { cx } from "cva";
-import { redirect } from "react-router-dom";
+import { redirect } from "react-router";
+import type { LoaderFunction } from "react-router";
import GridBackground from "@components/GridBackground";
import Container from "@components/Container";
@@ -17,7 +18,7 @@ export interface DeviceStatus {
isSetup: boolean;
}
-const loader = async () => {
+const loader: LoaderFunction = async () => {
const res = await api
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise);