From f8135263edd331140b19f03d0d3ee0802de779b1 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Fri, 22 Aug 2025 15:31:39 -0500 Subject: [PATCH] feature/Faster loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This refactors all the hot-path components for an already-setup JetKVM so that we only lazy-load the components off the main path. This greatly reduces the initial .JS size at initial page load from a single file of dist/assets/index-D4LZBdmN.js 1,969.46 kB │ gzip: 570.08 kB To these files, of which the hot-path only loads the 963.29 kB index for a savings of just over a megabyte (180kb savings in gzip). dist/assets/login-DA9KVVX1.js 0.64 kB │ gzip: 0.40 kB dist/assets/signup-Bb_VCzY1.js 0.67 kB │ gzip: 0.40 kB dist/assets/devices._id.settings.macros.add-DpBnq5E0.js 0.82 kB │ gzip: 0.55 kB dist/assets/devices._id.settings.appearance-VHd5B2H2.js 0.91 kB │ gzip: 0.52 kB dist/assets/devices._id.settings.general.reboot-DsRBP5Dd.js 1.01 kB │ gzip: 0.52 kB dist/assets/UpdateInProgressStatusCard-DJCdJo-z.js 1.05 kB │ gzip: 0.54 kB dist/assets/devices._id.other-session-BpXjEP6K.js 1.09 kB │ gzip: 0.56 kB dist/assets/devices.already-adopted-BC1xoKrN.js 1.16 kB │ gzip: 0.57 kB dist/assets/Checkbox-DGO277w5.js 1.24 kB │ gzip: 0.64 kB dist/assets/devices._id.settings.keyboard-Cno0kaUr.js 1.59 kB │ gzip: 0.81 kB dist/assets/devices._id.settings.general._index-CNW0Pj5B.js 1.71 kB │ gzip: 0.76 kB dist/assets/devices._id.settings.macros.edit-BYQGw2CJ.js 1.92 kB │ gzip: 1.00 kB dist/assets/ConfirmDialog-lzerZkf7.js 2.77 kB │ gzip: 1.13 kB dist/assets/AuthLayout-H4vGP3TU.js 2.96 kB │ gzip: 1.41 kB dist/assets/AutoHeight-B-TU1fRg.js 4.07 kB │ gzip: 1.63 kB dist/assets/devices._id.settings.video-O3qJWstQ.js 5.68 kB │ gzip: 2.17 kB dist/assets/devices._id.settings.advanced-Drd_iPzw.js 5.98 kB │ gzip: 2.08 kB dist/assets/devices._id.settings.macros-D3unB0uf.js 6.05 kB │ gzip: 2.13 kB dist/assets/devices._id.settings.access.local-auth-BltQI66N.js 6.17 kB │ gzip: 1.54 kB dist/assets/devices._id.settings.mouse-CAwDHqxl.js 10.02 kB │ gzip: 3.59 kB dist/assets/devices._id.settings.general.update-jkzXML1U.js 10.22 kB │ gzip: 2.67 kB dist/assets/devices._id.settings.hardware-B7v3lfwA.js 10.41 kB │ gzip: 3.03 kB dist/assets/devices._id.settings.network-CJYfzFt2.js 25.23 kB │ gzip: 7.21 kB dist/assets/devices._id.mount-4AT1reig.js 43.92 kB │ gzip: 19.81 kB dist/assets/MacroForm-BQpdQgFn.js 49.75 kB │ gzip: 16.25 kB dist/assets/connectionStats-NM-PZeH3.js 400.14 kB │ gzip: 110.33 kB dist/assets/Terminal-Dgo3sfr-.js 425.05 kB │ gzip: 109.49 kB dist/assets/index-w6H2Mz3f.js 963.29 kB │ gzip: 294.20 kB --- ui/src/main.tsx | 78 +++++++++---------- ui/src/routes/devices.$id.settings._index.tsx | 8 +- ui/src/routes/devices.$id.tsx | 30 ++++--- 3 files changed, 60 insertions(+), 56 deletions(-) diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 6fb5738..5374658 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -1,3 +1,4 @@ +import { lazy } from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; import { @@ -9,46 +10,45 @@ import { } from "react-router-dom"; import { ExclamationTriangleIcon } from "@heroicons/react/16/solid"; +import { CLOUD_API, DEVICE_API } from "@/ui.config"; +import api from "@/api"; +import Root from "@/root"; +import Card from "@components/Card"; import EmptyCard from "@components/EmptyCard"; import NotFoundPage from "@components/NotFoundPage"; +import DeviceRoute, { LocalDevice } from "@routes/devices.$id"; +import WelcomeRoute, { DeviceStatus } from "@routes/welcome-local"; +import LoginLocalRoute from "@routes/login-local"; +import WelcomeLocalModeRoute from "@routes/welcome-local.mode"; +import WelcomeLocalPasswordRoute from "@routes/welcome-local.password"; +import AdoptRoute from "@routes/adopt"; +import SetupRoute from "@routes/devices.$id.setup"; import DevicesIdDeregister from "@routes/devices.$id.deregister"; import DeviceIdRename from "@routes/devices.$id.rename"; -import AdoptRoute from "@routes/adopt"; -import SignupRoute from "@routes/signup"; -import LoginRoute from "@routes/login"; -import SetupRoute from "@routes/devices.$id.setup"; import DevicesRoute from "@routes/devices"; -import DeviceRoute, { LocalDevice } from "@routes/devices.$id"; -import Card from "@components/Card"; -import DevicesAlreadyAdopted from "@routes/devices.already-adopted"; - -import Root from "./root"; -import Notifications from "./notifications"; -import LoginLocalRoute from "./routes/login-local"; -import WelcomeLocalModeRoute from "./routes/welcome-local.mode"; -import WelcomeRoute, { DeviceStatus } from "./routes/welcome-local"; -import WelcomeLocalPasswordRoute from "./routes/welcome-local.password"; -import { CLOUD_API, DEVICE_API } from "./ui.config"; -import OtherSessionRoute from "./routes/devices.$id.other-session"; -import MountRoute from "./routes/devices.$id.mount"; -import * as SettingsRoute from "./routes/devices.$id.settings"; -import SettingsMouseRoute from "./routes/devices.$id.settings.mouse"; -import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard"; -import api from "./api"; -import * as SettingsIndexRoute from "./routes/devices.$id.settings._index"; -import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced"; -import SettingsAccessIndexRoute from "./routes/devices.$id.settings.access._index"; -import SettingsHardwareRoute from "./routes/devices.$id.settings.hardware"; -import SettingsVideoRoute from "./routes/devices.$id.settings.video"; -import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance"; -import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index"; -import SettingsGeneralRebootRoute from "./routes/devices.$id.settings.general.reboot"; -import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update"; -import SettingsNetworkRoute from "./routes/devices.$id.settings.network"; -import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth"; -import SettingsMacrosRoute from "./routes/devices.$id.settings.macros"; -import SettingsMacrosAddRoute from "./routes/devices.$id.settings.macros.add"; -import SettingsMacrosEditRoute from "./routes/devices.$id.settings.macros.edit"; +import SettingsIndexRoute from "@routes/devices.$id.settings._index"; +import SettingsAccessIndexRoute from "@routes/devices.$id.settings.access._index"; +const Notifications = lazy(() => import("@/notifications")); +const SignupRoute = lazy(() => import("@routes/signup")); +const LoginRoute = lazy(() => import("@routes/login")); +const DevicesAlreadyAdopted = lazy(() => import("@routes/devices.already-adopted")); +const OtherSessionRoute = lazy(() => import("@routes/devices.$id.other-session")); +const MountRoute = lazy(() => import("./routes/devices.$id.mount")); +const SettingsRoute = lazy(() => import("@routes/devices.$id.settings")); +const SettingsMouseRoute = lazy(() => import("@routes/devices.$id.settings.mouse")); +const SettingsKeyboardRoute = lazy(() => import("@routes/devices.$id.settings.keyboard")); +const SettingsAdvancedRoute = lazy(() => import("@routes/devices.$id.settings.advanced")); +const SettingsHardwareRoute = lazy(() => import("@routes/devices.$id.settings.hardware")); +const SettingsVideoRoute = lazy(() => import("@routes/devices.$id.settings.video")); +const SettingsAppearanceRoute = lazy(() => import("@routes/devices.$id.settings.appearance")); +const SettingsGeneralIndexRoute = lazy(() => import("@routes/devices.$id.settings.general._index")); +const SettingsGeneralRebootRoute = lazy(() => import("@routes/devices.$id.settings.general.reboot")); +const SettingsGeneralUpdateRoute = lazy(() => import("@routes/devices.$id.settings.general.update")); +const SettingsNetworkRoute = lazy(() => import("@routes/devices.$id.settings.network")); +const SecurityAccessLocalAuthRoute = lazy(() => import("@routes/devices.$id.settings.access.local-auth")); +const SettingsMacrosRoute = lazy(() => import("@routes/devices.$id.settings.macros")); +const SettingsMacrosAddRoute = lazy(() => import("@routes/devices.$id.settings.macros.add")); +const SettingsMacrosEditRoute = lazy(() => import("@routes/devices.$id.settings.macros.edit")); export const isOnDevice = import.meta.env.MODE === "device"; export const isInCloud = !isOnDevice; @@ -128,7 +128,7 @@ if (isOnDevice) { }, { path: "settings", - element: , + element: , children: [ { index: true, @@ -139,7 +139,7 @@ if (isOnDevice) { children: [ { index: true, - element: , + element: , }, { path: "reboot", @@ -265,7 +265,7 @@ if (isOnDevice) { }, { path: "settings", - element: , + element: , children: [ { index: true, @@ -276,7 +276,7 @@ if (isOnDevice) { children: [ { index: true, - element: , + element: , }, { path: "update", diff --git a/ui/src/routes/devices.$id.settings._index.tsx b/ui/src/routes/devices.$id.settings._index.tsx index 603efec..5203b4c 100644 --- a/ui/src/routes/devices.$id.settings._index.tsx +++ b/ui/src/routes/devices.$id.settings._index.tsx @@ -2,6 +2,12 @@ import { LoaderFunctionArgs, redirect } from "react-router-dom"; import { getDeviceUiPath } from "../hooks/useAppNavigation"; -export function loader({ params }: LoaderFunctionArgs) { +const loader = async ({ params }: LoaderFunctionArgs) => { return redirect(getDeviceUiPath("/settings/general", params.id)); } + +export default function SettingIndexRoute() { + return (<>); +} + +SettingIndexRoute.loader = loader; \ No newline at end of file diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index a4ecf3d..650bb14 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { LoaderFunctionArgs, Outlet, @@ -16,7 +16,11 @@ import { FocusTrap } from "focus-trap-react"; import { motion, AnimatePresence } from "framer-motion"; import useWebSocket from "react-use-websocket"; +import { CLOUD_API, DEVICE_API } from "@/ui.config"; +import api from "@/api"; +import { checkAuth, isInCloud, isOnDevice } from "@/main"; import { cx } from "@/cva.config"; +import notifications from "@/notifications"; import { HidState, KeyboardLedState, @@ -34,27 +38,21 @@ import { VideoState, } from "@/hooks/stores"; import WebRTCVideo from "@components/WebRTCVideo"; -import { checkAuth, isInCloud, isOnDevice } from "@/main"; import DashboardNavbar from "@components/Header"; -import ConnectionStatsSidebar from "@/components/sidebar/connectionStats"; +const ConnectionStatsSidebar = lazy(() => import('@/components/sidebar/connectionStats')); +const Terminal = lazy(() => import('@components/Terminal')); +const UpdateInProgressStatusCard = lazy(() => import("@/components/UpdateInProgressStatusCard")); +import Modal from "@/components/Modal"; import { JsonRpcRequest, JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc"; -import Terminal from "@components/Terminal"; -import { CLOUD_API, DEVICE_API } from "@/ui.config"; - -import UpdateInProgressStatusCard from "../components/UpdateInProgressStatusCard"; -import api from "../api"; -import Modal from "../components/Modal"; -import { useDeviceUiNavigation } from "../hooks/useAppNavigation"; import { ConnectionFailedOverlay, LoadingConnectionOverlay, PeerConnectionDisconnectedOverlay, -} from "../components/VideoOverlay"; -import { FeatureFlagProvider } from "../providers/FeatureFlagProvider"; -import notifications from "../notifications"; - -import { DeviceStatus } from "./welcome-local"; -import { SystemVersionInfo } from "./devices.$id.settings.general.update"; +} from "@/components/VideoOverlay"; +import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; +import { FeatureFlagProvider } from "@/providers/FeatureFlagProvider"; +import { DeviceStatus } from "@routes/welcome-local"; +import { SystemVersionInfo } from "@routes/devices.$id.settings.general.update"; interface LocalLoaderResp { authMode: "password" | "noPassword" | null;