Skip to content

Commit 05bf611

Browse files
authored
feature/Faster loading (#746)
* feature/Faster loading 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 * Remove feral async declarations on things that have no await
1 parent d952480 commit 05bf611

10 files changed

+75
-71
lines changed

ui/src/components/UsbDeviceSetting.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export function UsbDeviceSetting() {
127127
);
128128

129129
const handlePresetChange = useCallback(
130-
async (e: React.ChangeEvent<HTMLSelectElement>) => {
130+
(e: React.ChangeEvent<HTMLSelectElement>) => {
131131
const newPreset = e.target.value;
132132
setSelectedPreset(newPreset);
133133

ui/src/components/UsbInfoSetting.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export function UsbInfoSetting() {
137137
);
138138

139139
useEffect(() => {
140-
send("getDeviceID", {}, async (resp: JsonRpcResponse) => {
140+
send("getDeviceID", {}, (resp: JsonRpcResponse) => {
141141
if ("error" in resp) {
142142
return notifications.error(
143143
`Failed to get device ID: ${resp.error.data || "Unknown error"}`,

ui/src/components/WebRTCVideo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ export default function WebRTCVideo() {
411411
);
412412

413413
const keyDownHandler = useCallback(
414-
async (e: KeyboardEvent) => {
414+
(e: KeyboardEvent) => {
415415
e.preventDefault();
416416
const prev = useHidStore.getState();
417417
let code = e.code;

ui/src/main.tsx

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { lazy } from "react";
12
import ReactDOM from "react-dom/client";
23
import "./index.css";
34
import {
@@ -9,46 +10,45 @@ import {
910
} from "react-router-dom";
1011
import { ExclamationTriangleIcon } from "@heroicons/react/16/solid";
1112

13+
import { CLOUD_API, DEVICE_API } from "@/ui.config";
14+
import api from "@/api";
15+
import Root from "@/root";
16+
import Card from "@components/Card";
1217
import EmptyCard from "@components/EmptyCard";
1318
import NotFoundPage from "@components/NotFoundPage";
14-
import DevicesIdDeregister from "@routes/devices.$id.deregister";
15-
import DeviceIdRename from "@routes/devices.$id.rename";
19+
import DeviceRoute, { LocalDevice } from "@routes/devices.$id";
20+
import WelcomeRoute, { DeviceStatus } from "@routes/welcome-local";
21+
import LoginLocalRoute from "@routes/login-local";
22+
import WelcomeLocalModeRoute from "@routes/welcome-local.mode";
23+
import WelcomeLocalPasswordRoute from "@routes/welcome-local.password";
1624
import AdoptRoute from "@routes/adopt";
17-
import SignupRoute from "@routes/signup";
18-
import LoginRoute from "@routes/login";
1925
import SetupRoute from "@routes/devices.$id.setup";
26+
import DevicesIdDeregister from "@routes/devices.$id.deregister";
27+
import DeviceIdRename from "@routes/devices.$id.rename";
2028
import DevicesRoute from "@routes/devices";
21-
import DeviceRoute, { LocalDevice } from "@routes/devices.$id";
22-
import Card from "@components/Card";
23-
import DevicesAlreadyAdopted from "@routes/devices.already-adopted";
24-
25-
import Root from "./root";
26-
import Notifications from "./notifications";
27-
import LoginLocalRoute from "./routes/login-local";
28-
import WelcomeLocalModeRoute from "./routes/welcome-local.mode";
29-
import WelcomeRoute, { DeviceStatus } from "./routes/welcome-local";
30-
import WelcomeLocalPasswordRoute from "./routes/welcome-local.password";
31-
import { CLOUD_API, DEVICE_API } from "./ui.config";
32-
import OtherSessionRoute from "./routes/devices.$id.other-session";
33-
import MountRoute from "./routes/devices.$id.mount";
34-
import * as SettingsRoute from "./routes/devices.$id.settings";
35-
import SettingsMouseRoute from "./routes/devices.$id.settings.mouse";
36-
import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard";
37-
import api from "./api";
38-
import * as SettingsIndexRoute from "./routes/devices.$id.settings._index";
39-
import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced";
40-
import SettingsAccessIndexRoute from "./routes/devices.$id.settings.access._index";
41-
import SettingsHardwareRoute from "./routes/devices.$id.settings.hardware";
42-
import SettingsVideoRoute from "./routes/devices.$id.settings.video";
43-
import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance";
44-
import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index";
45-
import SettingsGeneralRebootRoute from "./routes/devices.$id.settings.general.reboot";
46-
import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update";
47-
import SettingsNetworkRoute from "./routes/devices.$id.settings.network";
48-
import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth";
49-
import SettingsMacrosRoute from "./routes/devices.$id.settings.macros";
50-
import SettingsMacrosAddRoute from "./routes/devices.$id.settings.macros.add";
51-
import SettingsMacrosEditRoute from "./routes/devices.$id.settings.macros.edit";
29+
import SettingsIndexRoute from "@routes/devices.$id.settings._index";
30+
import SettingsAccessIndexRoute from "@routes/devices.$id.settings.access._index";
31+
const Notifications = lazy(() => import("@/notifications"));
32+
const SignupRoute = lazy(() => import("@routes/signup"));
33+
const LoginRoute = lazy(() => import("@routes/login"));
34+
const DevicesAlreadyAdopted = lazy(() => import("@routes/devices.already-adopted"));
35+
const OtherSessionRoute = lazy(() => import("@routes/devices.$id.other-session"));
36+
const MountRoute = lazy(() => import("./routes/devices.$id.mount"));
37+
const SettingsRoute = lazy(() => import("@routes/devices.$id.settings"));
38+
const SettingsMouseRoute = lazy(() => import("@routes/devices.$id.settings.mouse"));
39+
const SettingsKeyboardRoute = lazy(() => import("@routes/devices.$id.settings.keyboard"));
40+
const SettingsAdvancedRoute = lazy(() => import("@routes/devices.$id.settings.advanced"));
41+
const SettingsHardwareRoute = lazy(() => import("@routes/devices.$id.settings.hardware"));
42+
const SettingsVideoRoute = lazy(() => import("@routes/devices.$id.settings.video"));
43+
const SettingsAppearanceRoute = lazy(() => import("@routes/devices.$id.settings.appearance"));
44+
const SettingsGeneralIndexRoute = lazy(() => import("@routes/devices.$id.settings.general._index"));
45+
const SettingsGeneralRebootRoute = lazy(() => import("@routes/devices.$id.settings.general.reboot"));
46+
const SettingsGeneralUpdateRoute = lazy(() => import("@routes/devices.$id.settings.general.update"));
47+
const SettingsNetworkRoute = lazy(() => import("@routes/devices.$id.settings.network"));
48+
const SecurityAccessLocalAuthRoute = lazy(() => import("@routes/devices.$id.settings.access.local-auth"));
49+
const SettingsMacrosRoute = lazy(() => import("@routes/devices.$id.settings.macros"));
50+
const SettingsMacrosAddRoute = lazy(() => import("@routes/devices.$id.settings.macros.add"));
51+
const SettingsMacrosEditRoute = lazy(() => import("@routes/devices.$id.settings.macros.edit"));
5252

5353
export const isOnDevice = import.meta.env.MODE === "device";
5454
export const isInCloud = !isOnDevice;
@@ -128,7 +128,7 @@ if (isOnDevice) {
128128
},
129129
{
130130
path: "settings",
131-
element: <SettingsRoute.default />,
131+
element: <SettingsRoute />,
132132
children: [
133133
{
134134
index: true,
@@ -139,7 +139,7 @@ if (isOnDevice) {
139139
children: [
140140
{
141141
index: true,
142-
element: <SettingsGeneralIndexRoute.default />,
142+
element: <SettingsGeneralIndexRoute />,
143143
},
144144
{
145145
path: "reboot",
@@ -265,7 +265,7 @@ if (isOnDevice) {
265265
},
266266
{
267267
path: "settings",
268-
element: <SettingsRoute.default />,
268+
element: <SettingsRoute />,
269269
children: [
270270
{
271271
index: true,
@@ -276,7 +276,7 @@ if (isOnDevice) {
276276
children: [
277277
{
278278
index: true,
279-
element: <SettingsGeneralIndexRoute.default />,
279+
element: <SettingsGeneralIndexRoute />,
280280
},
281281
{
282282
path: "update",

ui/src/routes/devices.$id.mount.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export function Dialog({ onClose }: { onClose: () => void }) {
8989
console.log(`Mounting ${url} as ${mode}`);
9090

9191
setMountInProgress(true);
92-
send("mountWithHTTP", { url, mode }, async (resp: JsonRpcResponse) => {
92+
send("mountWithHTTP", { url, mode }, (resp: JsonRpcResponse) => {
9393
if ("error" in resp) triggerError(resp.error.message);
9494

9595
clearMountMediaState();
@@ -108,7 +108,7 @@ export function Dialog({ onClose }: { onClose: () => void }) {
108108
console.log(`Mounting ${fileName} as ${mode}`);
109109

110110
setMountInProgress(true);
111-
send("mountWithStorage", { filename: fileName, mode }, async (resp: JsonRpcResponse) => {
111+
send("mountWithStorage", { filename: fileName, mode }, (resp: JsonRpcResponse) => {
112112
if ("error" in resp) triggerError(resp.error.message);
113113

114114
clearMountMediaState();

ui/src/routes/devices.$id.settings._index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { LoaderFunctionArgs, redirect } from "react-router-dom";
22

33
import { getDeviceUiPath } from "../hooks/useAppNavigation";
44

5-
export function loader({ params }: LoaderFunctionArgs) {
5+
const loader = ({ params }: LoaderFunctionArgs) => {
66
return redirect(getDeviceUiPath("/settings/general", params.id));
77
}
8+
9+
export default function SettingIndexRoute() {
10+
return (<></>);
11+
}
12+
13+
SettingIndexRoute.loader = loader;

ui/src/routes/devices.$id.settings.access._index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default function SettingsAccessIndexRoute() {
8787
});
8888
}, [send]);
8989

90-
const deregisterDevice = async () => {
90+
const deregisterDevice = () => {
9191
send("deregisterDevice", {}, (resp: JsonRpcResponse) => {
9292
if ("error" in resp) {
9393
notifications.error(
@@ -198,7 +198,7 @@ export default function SettingsAccessIndexRoute() {
198198
getCloudState();
199199
getTLSState();
200200

201-
send("getDeviceID", {}, async (resp: JsonRpcResponse) => {
201+
send("getDeviceID", {}, (resp: JsonRpcResponse) => {
202202
if ("error" in resp) return console.error(resp.error);
203203
setDeviceId(resp.result as string);
204204
});

ui/src/routes/devices.$id.settings.general.update.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function Dialog({
6262
const { modalView, setModalView, otaState } = useUpdateStore();
6363

6464
const onFinishedLoading = useCallback(
65-
async (versionInfo: SystemVersionInfo) => {
65+
(versionInfo: SystemVersionInfo) => {
6666
const hasUpdate =
6767
versionInfo?.systemUpdateAvailable || versionInfo?.appUpdateAvailable;
6868

@@ -141,7 +141,7 @@ function LoadingState({
141141

142142
const getVersionInfo = useCallback(() => {
143143
return new Promise<SystemVersionInfo>((resolve, reject) => {
144-
send("getUpdateStatus", {}, async (resp: JsonRpcResponse) => {
144+
send("getUpdateStatus", {}, (resp: JsonRpcResponse) => {
145145
if ("error" in resp) {
146146
notifications.error(`Failed to check for updates: ${resp.error}`);
147147
reject(new Error("Failed to check for updates"));

ui/src/routes/devices.$id.settings.mouse.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ export default function SettingsMouseRoute() {
121121
const saveJigglerConfig = useCallback(
122122
(jigglerConfig: JigglerConfig) => {
123123
// We assume the jiggler should be set to enabled if the config is being updated
124-
send("setJigglerState", { enabled: true }, async (resp: JsonRpcResponse) => {
124+
send("setJigglerState", { enabled: true }, (resp: JsonRpcResponse) => {
125125
if ("error" in resp) {
126126
return notifications.error(
127127
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
128128
);
129129
}
130130
});
131131

132-
send("setJigglerConfig", { jigglerConfig }, async (resp: JsonRpcResponse) => {
132+
send("setJigglerConfig", { jigglerConfig }, (resp: JsonRpcResponse) => {
133133
if ("error" in resp) {
134134
const errorMsg = resp.error.data || "Unknown error";
135135

@@ -163,7 +163,7 @@ export default function SettingsMouseRoute() {
163163

164164
// We don't need to update the device jiggler state when the option is "disabled"
165165
if (option === "disabled") {
166-
send("setJigglerState", { enabled: false }, async (resp: JsonRpcResponse) => {
166+
send("setJigglerState", { enabled: false }, (resp: JsonRpcResponse) => {
167167
if ("error" in resp) {
168168
return notifications.error(
169169
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,

ui/src/routes/devices.$id.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1+
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import {
33
LoaderFunctionArgs,
44
Outlet,
@@ -16,7 +16,11 @@ import { FocusTrap } from "focus-trap-react";
1616
import { motion, AnimatePresence } from "framer-motion";
1717
import useWebSocket from "react-use-websocket";
1818

19+
import { CLOUD_API, DEVICE_API } from "@/ui.config";
20+
import api from "@/api";
21+
import { checkAuth, isInCloud, isOnDevice } from "@/main";
1922
import { cx } from "@/cva.config";
23+
import notifications from "@/notifications";
2024
import {
2125
HidState,
2226
KeyboardLedState,
@@ -34,27 +38,21 @@ import {
3438
VideoState,
3539
} from "@/hooks/stores";
3640
import WebRTCVideo from "@components/WebRTCVideo";
37-
import { checkAuth, isInCloud, isOnDevice } from "@/main";
3841
import DashboardNavbar from "@components/Header";
39-
import ConnectionStatsSidebar from "@/components/sidebar/connectionStats";
42+
const ConnectionStatsSidebar = lazy(() => import('@/components/sidebar/connectionStats'));
43+
const Terminal = lazy(() => import('@components/Terminal'));
44+
const UpdateInProgressStatusCard = lazy(() => import("@/components/UpdateInProgressStatusCard"));
45+
import Modal from "@/components/Modal";
4046
import { JsonRpcRequest, JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
41-
import Terminal from "@components/Terminal";
42-
import { CLOUD_API, DEVICE_API } from "@/ui.config";
43-
44-
import UpdateInProgressStatusCard from "../components/UpdateInProgressStatusCard";
45-
import api from "../api";
46-
import Modal from "../components/Modal";
47-
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
4847
import {
4948
ConnectionFailedOverlay,
5049
LoadingConnectionOverlay,
5150
PeerConnectionDisconnectedOverlay,
52-
} from "../components/VideoOverlay";
53-
import { FeatureFlagProvider } from "../providers/FeatureFlagProvider";
54-
import notifications from "../notifications";
55-
56-
import { DeviceStatus } from "./welcome-local";
57-
import { SystemVersionInfo } from "./devices.$id.settings.general.update";
51+
} from "@/components/VideoOverlay";
52+
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
53+
import { FeatureFlagProvider } from "@/providers/FeatureFlagProvider";
54+
import { DeviceStatus } from "@routes/welcome-local";
55+
import { SystemVersionInfo } from "@routes/devices.$id.settings.general.update";
5856

5957
interface LocalLoaderResp {
6058
authMode: "password" | "noPassword" | null;
@@ -114,7 +112,7 @@ const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> =>
114112
return { user, iceConfig, deviceName: device.name || device.id };
115113
};
116114

117-
const loader = async ({ params }: LoaderFunctionArgs) => {
115+
const loader = ({ params }: LoaderFunctionArgs) => {
118116
return import.meta.env.MODE === "device" ? deviceLoader() : cloudLoader(params);
119117
};
120118

@@ -452,7 +450,7 @@ export default function KvmIdRoute() {
452450
}
453451
};
454452

455-
pc.onicecandidate = async ({ candidate }) => {
453+
pc.onicecandidate = ({ candidate }) => {
456454
if (!candidate) return;
457455
if (candidate.candidate === "") return;
458456
sendWebRTCSignal("new-ice-candidate", candidate);
@@ -735,7 +733,7 @@ export default function KvmIdRoute() {
735733
useEffect(() => {
736734
if (appVersion) return;
737735

738-
send("getUpdateStatus", {}, async (resp: JsonRpcResponse) => {
736+
send("getUpdateStatus", {}, (resp: JsonRpcResponse) => {
739737
if ("error" in resp) {
740738
notifications.error(`Failed to get device version: ${resp.error}`);
741739
return

0 commit comments

Comments
 (0)