-
-
USB Emulation Configuration
-
- Set custom USB parameters to control how the USB device is emulated.
- The device will rebind once the parameters are updated.
-
-
-
- Look up USB Device IDs here
-
-
-
+
+
handleUsbProduct(e.target.value)}
/>
-
- onSetUsbConfig(usbConfigState)}
- />
-
-
- {error && {error}
}
+
+
+ onSetUsbConfig(usbConfigState)}
+ />
+
);
}
-
-function SuccessModal({
- headline,
- description,
- onClose,
-}: {
- headline: string;
- description: string;
- onClose: () => void;
-}) {
- return (
-
-
-
-
-
-
-
-
{headline}
-
{description}
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/ui/src/components/sidebar/settings.tsx b/ui/src/components/sidebar/settings.tsx
deleted file mode 100644
index fb19f89..0000000
--- a/ui/src/components/sidebar/settings.tsx
+++ /dev/null
@@ -1,1254 +0,0 @@
-import SidebarHeader from "@components/SidebarHeader";
-import {
- BacklightSettings,
- useLocalAuthModalStore,
- useSettingsStore,
- useUiStore,
- useUpdateStore,
- useUsbConfigModalStore
-} from "@/hooks/stores";
-import { Checkbox } from "@components/Checkbox";
-import { Button, LinkButton } from "@components/Button";
-import { TextAreaWithLabel } from "@components/TextArea";
-import { SectionHeader } from "@components/SectionHeader";
-import { GridCard } from "@components/Card";
-import { CheckCircleIcon } from "@heroicons/react/20/solid";
-import { cx } from "@/cva.config";
-import React, {useCallback, useEffect, useMemo, useRef, useState} from "react";
-import { isOnDevice } from "@/main";
-import PointingFinger from "@/assets/pointing-finger.svg";
-import MouseIcon from "@/assets/mouse-icon.svg";
-import { useJsonRpc } from "@/hooks/useJsonRpc";
-import { SelectMenuBasic } from "../SelectMenuBasic";
-import { SystemVersionInfo } from "@components/UpdateDialog";
-import notifications from "@/notifications";
-import api from "../../api";
-import LocalAuthPasswordDialog from "@/components/LocalAuthPasswordDialog";
-import { LocalDevice } from "@routes/devices.$id";
-import { useRevalidator } from "react-router-dom";
-import { ShieldCheckIcon } from "@heroicons/react/20/solid";
-import USBConfigDialog from "@components/USBConfigDialog";
-import { UsbConfigState } from "@/hooks/stores"
-import { CLOUD_APP, DEVICE_API } from "@/ui.config";
-import { InputFieldWithLabel } from "../InputField";
-
-export function SettingsItem({
- title,
- description,
- children,
- className,
-}: {
- title: string;
- description: string | React.ReactNode;
- children?: React.ReactNode;
- className?: string;
- name?: string;
-}) {
- return (
-
-
-
{title}
-
{description}
-
- {children ? {children}
: null}
-
- );
-}
-
-
-const generatedSerialNumber = [generateNumber(1,9), generateHex(7,7), 0, 1].join("&");
-
-function generateNumber(min: number, max: number) {
- return Math.floor(Math.random() * (max - min + 1) + min);
-}
-
-function generateHex(min: number, max: number) {
- const len = generateNumber(min, max);
- const n = (Math.random() * 0xfffff * 1000000).toString(16);
- return n.slice(0, len);
-}
-
-const defaultEdid =
- "00ffffffffffff0052620188008888881c150103800000780a0dc9a05747982712484c00000001010101010101010101010101010101023a801871382d40582c4500c48e2100001e011d007251d01e206e285500c48e2100001e000000fc00543734392d6648443732300a20000000fd00147801ff1d000a202020202020017b";
-const edids = [
- {
- value: defaultEdid,
- label: "JetKVM Default",
- },
- {
- value:
- "00FFFFFFFFFFFF00047265058A3F6101101E0104A53420783FC125A8554EA0260D5054BFEF80714F8140818081C081008B009500B300283C80A070B023403020360006442100001A000000FD00304C575716010A202020202020000000FC0042323436574C0A202020202020000000FF0054384E4545303033383532320A01F802031CF14F90020304050607011112131415161F2309070783010000011D8018711C1620582C250006442100009E011D007251D01E206E28550006442100001E8C0AD08A20E02D10103E9600064421000018C344806E70B028401720A80406442100001E00000000000000000000000000000000000000000000000000000096",
- label: "Acer B246WL, 1920x1200",
- },
- {
- value:
- "00FFFFFFFFFFFF0006B3872401010101021F010380342078EA6DB5A7564EA0250D5054BF6F00714F8180814081C0A9409500B300D1C0283C80A070B023403020360006442100001A000000FD00314B1E5F19000A202020202020000000FC00504132343851560A2020202020000000FF004D314C4D51533035323135370A014D02032AF14B900504030201111213141F230907078301000065030C001000681A00000101314BE6E2006A023A801871382D40582C450006442100001ECD5F80B072B0374088D0360006442100001C011D007251D01E206E28550006442100001E8C0AD08A20E02D10103E960006442100001800000000000000000000000000DC",
- label: "ASUS PA248QV, 1920x1200",
- },
- {
- value:
- "00FFFFFFFFFFFF0010AC132045393639201E0103803C22782ACD25A3574B9F270D5054A54B00714F8180A9C0D1C00101010101010101023A801871382D40582C450056502100001E000000FF00335335475132330A2020202020000000FC0044454C4C204432373231480A20000000FD00384C1E5311000A202020202020018102031AB14F90050403020716010611121513141F65030C001000023A801871382D40582C450056502100001E011D8018711C1620582C250056502100009E011D007251D01E206E28550056502100001E8C0AD08A20E02D10103E960056502100001800000000000000000000000000000000000000000000000000000000004F",
- label: "DELL D2721H, 1920x1080",
- },
-];
-
-export default function SettingsSidebar() {
- const setSidebarView = useUiStore(state => state.setSidebarView);
- const settings = useSettingsStore();
- const [send] = useJsonRpc();
- const [streamQuality, setStreamQuality] = useState("1");
- const [autoUpdate, setAutoUpdate] = useState(true);
- const [devChannel, setDevChannel] = useState(false);
- const [jiggler, setJiggler] = useState(false);
- const [edid, setEdid] = useState
(null);
- const [customEdidValue, setCustomEdidValue] = useState(null);
- const [usbConfigProduct, setUsbConfigProduct] = useState("");
-
- const [isAdopted, setAdopted] = useState(false);
- const [deviceId, setDeviceId] = useState(null);
-
- const [sshKey, setSSHKey] = useState("");
- const [localDevice, setLocalDevice] = useState(null);
-
- const sidebarRef = useRef(null);
-
- const hideCursor = useSettingsStore(state => state.isCursorHidden);
- const setHideCursor = useSettingsStore(state => state.setCursorVisibility);
- const setDeveloperMode = useSettingsStore(state => state.setDeveloperMode);
- const setBacklightSettings = useSettingsStore(state => state.setBacklightSettings);
-
- const [currentVersions, setCurrentVersions] = useState<{
- appVersion: string;
- systemVersion: string;
- } | null>(null);
-
- const [usbEmulationEnabled, setUsbEmulationEnabled] = useState(false);
- const getUsbEmulationState = useCallback(() => {
- send("getUsbEmulationState", {}, resp => {
- if ("error" in resp) return;
- setUsbEmulationEnabled(resp.result as boolean);
- });
- }, [send]);
-
- const usbConfigs = useMemo(() => [
- {
- label: "JetKVM Default",
- value: "JetKVM USB Emulation Device"
- },
- {
- label: "Logitech Universal Adapter",
- value: "Logitech USB Input Device"
- },
- {
- label: "Microsoft Wireless MultiMedia Keyboard",
- value: "Wireless MultiMedia Keyboard"
- },
- {
- label: "Dell Multimedia Pro Keyboard",
- value: "Multimedia Pro Keyboard"
- }
- ], []);
-
-
- interface USBConfig {
- vendor_id: string;
- product_id: string;
- serial_number: string | null;
- manufacturer: string;
- product: string;
- }
-
- type UsbConfigMap = Record;
-
-
- const usbConfigData: UsbConfigMap = {
- "JetKVM USB Emulation Device": {
- vendor_id: "0x1d6b",
- product_id: "0x0104",
- serial_number: deviceId,
- manufacturer: "JetKVM",
- product: "JetKVM USB Emulation Device",
- },
- "Logitech USB Input Device": {
- vendor_id: "0x046d",
- product_id: "0xc52b",
- serial_number: generatedSerialNumber,
- manufacturer: "Logitech (x64)",
- product: "Logitech USB Input Device",
- },
- "Wireless MultiMedia Keyboard": {
- vendor_id: "0x045e",
- product_id: "0x005f",
- serial_number: generatedSerialNumber,
- manufacturer: "Microsoft",
- product: "Wireless MultiMedia Keyboard",
- },
- "Multimedia Pro Keyboard": {
- vendor_id: "0x413c",
- product_id: "0x2011",
- serial_number: generatedSerialNumber,
- manufacturer: "Dell Inc.",
- product: "Multimedia Pro Keyboard",
- }
- }
-
- const syncUsbConfigProduct = useCallback(() => {
- send("getUsbConfig", {}, resp => {
- if ("error" in resp) {
- console.error("Failed to load USB Config:", resp.error);
- } else {
- console.log("syncUsbConfigProduct#getUsbConfig result:", resp.result);
- const usbConfigState = resp.result as UsbConfigState
- const product = usbConfigs.map(u => u.value).includes(usbConfigState.product) ? usbConfigState.product : "custom"
- setUsbConfigProduct(product);
- }
- });
- }, [send, usbConfigs]);
-
- // Load stored usb config product from the backend
- useEffect(() => {
- syncUsbConfigProduct();
- }, [syncUsbConfigProduct]);
-
- const handleUsbEmulationToggle = useCallback(
- (enabled: boolean) => {
- send("setUsbEmulationState", { enabled: enabled }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to ${enabled ? "enable" : "disable"} USB emulation: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setUsbEmulationEnabled(enabled);
- getUsbEmulationState();
- });
- },
- [getUsbEmulationState, send],
- );
-
- const getCloudState = useCallback(() => {
- send("getCloudState", {}, resp => {
- if ("error" in resp) return console.error(resp.error);
- const cloudState = resp.result as { connected: boolean };
- setAdopted(cloudState.connected);
- });
- }, [send]);
-
- const deregisterDevice = async () => {
- send("deregisterDevice", {}, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to de-register device: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- getCloudState();
- return;
- });
- };
-
- const handleStreamQualityChange = (factor: string) => {
- send("setStreamQualityFactor", { factor: Number(factor) }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set stream quality: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setStreamQuality(factor);
- });
- };
-
- const handleAutoUpdateChange = (enabled: boolean) => {
- send("setAutoUpdateState", { enabled }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set auto-update: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setAutoUpdate(enabled);
- });
- };
-
- const handleDevChannelChange = (enabled: boolean) => {
- send("setDevChannelState", { enabled }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set dev channel state: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setDevChannel(enabled);
- });
- };
-
- const handleUsbConfigChange = (product: string) => {
- const usbConfig = usbConfigData[product];
- console.info(`USB config: ${JSON.stringify(usbConfig)}`)
- send("setUsbConfig", { usbConfig }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set usb config: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setUsbConfigProduct(usbConfig.product);
- notifications.success(`USB Config set to ${usbConfig.manufacturer} ${usbConfig.product}`);
- });
- };
-
- const handleJigglerChange = (enabled: boolean) => {
- send("setJigglerState", { enabled }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setJiggler(enabled);
- });
- };
-
- const handleEDIDChange = (newEdid: string) => {
- send("setEDID", { edid: newEdid }, resp => {
- if ("error" in resp) {
- notifications.error(`Failed to set EDID: ${resp.error.data || "Unknown error"}`);
- return;
- }
-
- // Update the EDID value in the UI
- setEdid(newEdid);
- });
- };
-
- const handleSSHKeyChange = (newKey: string) => {
- setSSHKey(newKey);
- };
-
- const handleDevModeChange = useCallback(
- (developerMode: boolean) => {
- send("setDevModeState", { enabled: developerMode }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set dev mode: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- setDeveloperMode(developerMode);
- setTimeout(() => {
- sidebarRef.current?.scrollTo({ top: 5000, behavior: "smooth" });
- }, 0);
- });
- },
- [send, setDeveloperMode],
- );
-
- const handleBacklightSettingsChange = (settings: BacklightSettings) => {
- // If the user has set the display to dim after it turns off, set the dim_after
- // value to never.
- if (settings.dim_after > settings.off_after && settings.off_after != 0) {
- settings.dim_after = 0;
- }
-
- setBacklightSettings(settings);
- handleBacklightSettingsSave();
- }
-
- const handleBacklightSettingsSave = () => {
- send("setBacklightSettings", { params: settings.backlightSettings }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to set backlight settings: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- notifications.success("Backlight settings updated successfully");
- });
- };
-
- const handleUpdateSSHKey = useCallback(() => {
- send("setSSHKeyState", { sshKey }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to update SSH key: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- notifications.success("SSH key updated successfully");
- });
- }, [send, sshKey]);
-
- const { setIsUpdateDialogOpen, setModalView, otaState } = useUpdateStore();
- const handleCheckForUpdates = () => {
- if (otaState.updating) {
- setModalView("updating");
- setIsUpdateDialogOpen(true);
- } else {
- setModalView("loading");
- setIsUpdateDialogOpen(true);
- }
- };
-
- const [cloudUrl, setCloudUrl] = useState("");
-
- useEffect(() => {
- send("getCloudUrl", {}, resp => {
- if ("error" in resp) return;
- setCloudUrl(resp.result as string);
- });
- }, [send]);
-
- const getCloudUrl = useCallback(() => {
- send("getCloudUrl", {}, resp => {
- if ("error" in resp) return;
- setCloudUrl(resp.result as string);
- });
- }, [send]);
-
- const handleCloudUrlChange = useCallback(
- (url: string) => {
- send("setCloudUrl", { url }, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to update cloud URL: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- getCloudUrl();
- notifications.success("Cloud URL updated successfully");
- });
- },
- [send, getCloudUrl],
- );
-
- const handleResetCloudUrl = useCallback(() => {
- send("resetCloudUrl", {}, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to reset cloud URL: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- getCloudUrl();
- notifications.success("Cloud URL reset to default successfully");
- });
- }, [send, getCloudUrl]);
-
- useEffect(() => {
- getCloudState();
-
- send("getDeviceID", {}, async resp => {
- if ("error" in resp) return console.error(resp.error);
- setDeviceId(resp.result as string);
- });
-
- send("getJigglerState", {}, resp => {
- if ("error" in resp) return;
- setJiggler(resp.result as boolean);
- });
-
- send("getAutoUpdateState", {}, resp => {
- if ("error" in resp) return;
- setAutoUpdate(resp.result as boolean);
- });
-
- send("getDevChannelState", {}, resp => {
- if ("error" in resp) return;
- setDevChannel(resp.result as boolean);
- });
-
- send("getStreamQualityFactor", {}, resp => {
- if ("error" in resp) return;
- setStreamQuality(String(resp.result));
- });
-
- send("getEDID", {}, resp => {
- if ("error" in resp) {
- notifications.error(`Failed to get EDID: ${resp.error.data || "Unknown error"}`);
- return;
- }
-
- const receivedEdid = resp.result as string;
-
- const matchingEdid = edids.find(
- x => x.value.toLowerCase() === receivedEdid.toLowerCase(),
- );
-
- if (matchingEdid) {
- // EDID is stored in uppercase in the UI
- setEdid(matchingEdid.value.toUpperCase());
- // Reset custom EDID value
- setCustomEdidValue(null);
- } else {
- setEdid("custom");
- setCustomEdidValue(receivedEdid);
- }
- });
-
- send("getBacklightSettings", {}, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to get backlight settings: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- const result = resp.result as BacklightSettings;
- setBacklightSettings(result);
- })
-
- send("getDevModeState", {}, resp => {
- if ("error" in resp) return;
- const result = resp.result as { enabled: boolean };
- setDeveloperMode(result.enabled);
- });
-
- send("getSSHKeyState", {}, resp => {
- if ("error" in resp) return;
- setSSHKey(resp.result as string);
- });
-
- send("getUpdateStatus", {}, resp => {
- if ("error" in resp) return;
- const result = resp.result as SystemVersionInfo;
- setCurrentVersions({
- appVersion: result.local.appVersion,
- systemVersion: result.local.systemVersion,
- });
- });
-
- send("getUsbEmulationState", {}, resp => {
- if ("error" in resp) return;
- setUsbEmulationEnabled(resp.result as boolean);
- });
- }, [
- getCloudState,
- send,
- setBacklightSettings,
- setDeveloperMode,
- setHideCursor,
- setJiggler,
- ]);
-
- const getDevice = useCallback(async () => {
- try {
- const status = await api
- .GET(`${DEVICE_API}/device`)
- .then(res => res.json() as Promise);
- setLocalDevice(status);
- } catch (error) {
- notifications.error("Failed to get authentication status");
- }
- }, []);
-
- const { setModalView: setLocalAuthModalView } = useLocalAuthModalStore();
- const { setModalView: setUsbConfigModalView } = useUsbConfigModalStore();
- const [isLocalAuthDialogOpen, setIsLocalAuthDialogOpen] = useState(false);
- const [isUsbConfigDialogOpen, setIsUsbConfigDialogOpen] = useState(false);
-
- useEffect(() => {
- if (isOnDevice) getDevice();
- }, [getDevice]);
-
- useEffect(() => {
- if (!isOnDevice) return;
- // Refresh device status when the local auth dialog is closed
- if (!isLocalAuthDialogOpen) {
- getDevice();
- }
- }, [getDevice, isLocalAuthDialogOpen]);
-
- useEffect(() => {
- if (!isOnDevice) return;
- // Refresh device status when the local usb config dialog is closed
- if (!isUsbConfigDialogOpen) {
- getDevice();
- }
- }, [getDevice, isUsbConfigDialogOpen]);
-
- const revalidator = useRevalidator();
-
- const [currentTheme, setCurrentTheme] = useState(() => {
- return localStorage.theme || "system";
- });
-
- const handleThemeChange = useCallback((value: string) => {
- const root = document.documentElement;
-
- if (value === "system") {
- localStorage.removeItem("theme");
- // Check system preference
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
- ? "dark"
- : "light";
- root.classList.remove("light", "dark");
- root.classList.add(systemTheme);
- } else {
- localStorage.theme = value;
- root.classList.remove("light", "dark");
- root.classList.add(value);
- }
- }, []);
-
- const handleResetConfig = useCallback(() => {
- send("resetConfig", {}, resp => {
- if ("error" in resp) {
- notifications.error(
- `Failed to reset configuration: ${resp.error.data || "Unknown error"}`,
- );
- return;
- }
- notifications.success("Configuration reset to default successfully");
- });
- }, [send]);
-
- return (
- e.stopPropagation()}
- onKeyUp={e => e.stopPropagation()}
- >
-
-
-
-
-
- App: {currentVersions.appVersion}
-
- System: {currentVersions.systemVersion}
- >
- ) : (
- "Loading current versions..."
- )
- }
- />
-
-
-
-
-
-
-
-
-
- {
- setHideCursor(e.target.checked);
- }}
- />
-
-
- {
- handleJigglerChange(e.target.checked);
- }}
- />
-
-
-
-
-
console.log("Absolute mouse mode clicked")}
- >
-
-
-
-
-
-
- Absolute
-
-
- Most convenient
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Relative
-
-
- Coming soon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- handleStreamQualityChange(e.target.value)}
- />
-
-
- {
- if (e.target.value === "custom") {
- setEdid("custom");
- setCustomEdidValue("");
- } else {
- handleEDIDChange(e.target.value as string);
- }
- }}
- options={[...edids, { value: "custom", label: "Custom" }]}
- />
-
- {customEdidValue !== null && (
- <>
-
-
setCustomEdidValue(e.target.value)}
- />
-
- handleEDIDChange(customEdidValue)}
- />
- {
- setCustomEdidValue(null);
- handleEDIDChange(defaultEdid);
- }}
- />
-
- >
- )}
-
-
- {isOnDevice && (
- <>
-
-
-
-
-
-
-
-
-
-
- Cloud Security
-
-
-
- • End-to-end encryption using WebRTC (DTLS and SRTP)
- • Zero Trust security model
- • OIDC (OpenID Connect) authentication
- • All streams encrypted in transit
-
-
-
-
- All cloud components are open-source and available on{" "}
-
- GitHub
-
- .
-
-
-
-
-
-
-
-
-
-
-
- {!isAdopted ? (
-
-
-
- ) : (
-
-
-
- Your device is adopted to JetKVM Cloud
-
-
- {
- if (deviceId) {
- if (
- window.confirm(
- "Are you sure you want to de-register this device?",
- )
- ) {
- deregisterDevice();
- }
- } else {
- notifications.error("No device ID available");
- }
- }}
- />
-
-
-
- )}
-
- >
- )}
-
- {isOnDevice ? (
- <>
-
-
-
-
-
- {localDevice?.authMode === "password" ? (
- {
- setLocalAuthModalView("deletePassword");
- setIsLocalAuthDialogOpen(true);
- }}
- />
- ) : (
- {
- setLocalAuthModalView("createPassword");
- setIsLocalAuthDialogOpen(true);
- }}
- />
- )}
-
-
- {localDevice?.authMode === "password" && (
-
- {
- setLocalAuthModalView("updatePassword");
- setIsLocalAuthDialogOpen(true);
- }}
- />
-
- )}
-
-
-
- >
- ) : null}
-
-
-
-
-
- {
- handleAutoUpdateChange(e.target.checked);
- }}
- />
-
-
- {
- handleDevChannelChange(e.target.checked);
- }}
- />
-
-
-
-
-
-
-
- {
- setCurrentTheme(e.target.value);
- handleThemeChange(e.target.value);
- }}
- />
-
-
-
-
-
-
- {
- settings.backlightSettings.max_brightness = parseInt(e.target.value)
- handleBacklightSettingsChange(settings.backlightSettings);
- }}
- />
-
- {settings.backlightSettings.max_brightness != 0 && (
- <>
-
- {
- settings.backlightSettings.dim_after = parseInt(e.target.value)
- handleBacklightSettingsChange(settings.backlightSettings);
- }}
- />
-
-
- {
- settings.backlightSettings.off_after = parseInt(e.target.value)
- handleBacklightSettingsChange(settings.backlightSettings);
- }}
- />
-
- >
- )}
-
- The display will wake up when the connection state changes, or when touched.
-
-
- {
- if (e.target.value === "custom") {
- setUsbConfigProduct(e.target.value);
- } else {
- handleUsbConfigChange(e.target.value as string);
- }
- }}
- options={[...usbConfigs, { value: "custom", label: "Custom" }]}
- />
-
- {(usbConfigProduct === "custom") && (
-
- {
- setUsbConfigModalView("updateUsbConfig")
- setIsUsbConfigDialogOpen(true);
- }}
- />
-
- )}
-
-
-
-
-
-
- handleDevModeChange(e.target.checked)}
- />
-
-
- {settings.developerMode && (
-
-
-
handleSSHKeyChange(e.target.value)}
- placeholder="Enter your SSH public key"
- />
-
- The default SSH user is root .
-
-
-
-
-
- {isOnDevice && (
-
-
-
-
setCloudUrl(e.target.value)}
- placeholder="https://api.jetkvm.com"
- />
-
-
- handleCloudUrlChange(cloudUrl)}
- />
-
-
-
- )}
-
- )}
-
- {
- settings.setDebugMode(e.target.checked);
- }}
- />
-
- {settings.debugMode && (
- <>
-
- handleUsbEmulationToggle(!usbEmulationEnabled)}
- />
-
- >
- )}
- {settings.debugMode && (
-
- {
- handleResetConfig();
- window.location.reload();
- }}
- />
-
- )}
-
-
-
-
{
- // Revalidate the current route to refresh the local device status and dependent UI components
- revalidator.revalidate();
- setIsUsbConfigDialogOpen(x);
- }}
- />
- {
- // Revalidate the current route to refresh the local device status and dependent UI components
- revalidator.revalidate();
- setIsLocalAuthDialogOpen(x);
- }}
- />
-
- );
-}
diff --git a/ui/src/hooks/useJsonRpc.ts b/ui/src/hooks/useJsonRpc.ts
index da53e04..f3390f7 100644
--- a/ui/src/hooks/useJsonRpc.ts
+++ b/ui/src/hooks/useJsonRpc.ts
@@ -8,17 +8,25 @@ export interface JsonRpcRequest {
id: number | string;
}
-type JsonRpcResponse =
- | {
- jsonrpc: string;
- result: boolean | number | object | string | [];
- id: string | number;
- }
- | {
- jsonrpc: string;
- error: { code: number; data?: string; message: string };
- id: string | number;
- };
+export interface JsonRpcError {
+ code: number;
+ data?: string;
+ message: string;
+}
+
+export interface JsonRpcSuccessResponse {
+ jsonrpc: string;
+ result: boolean | number | object | string | [];
+ id: string | number;
+}
+
+export interface JsonRpcErrorResponse {
+ jsonrpc: string;
+ error: JsonRpcError;
+ id: string | number;
+}
+
+export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
const callbackStore = new Map void>();
let requestCounter = 0;
diff --git a/ui/src/routes/devices.$id.settings.advanced.tsx b/ui/src/routes/devices.$id.settings.advanced.tsx
index 71ae244..ae8996e 100644
--- a/ui/src/routes/devices.$id.settings.advanced.tsx
+++ b/ui/src/routes/devices.$id.settings.advanced.tsx
@@ -11,7 +11,7 @@ import { isOnDevice } from "../main";
import { InputFieldWithLabel } from "../components/InputField";
import { Button } from "../components/Button";
import { useSettingsStore } from "../hooks/stores";
-import { GridCard } from "@/components/Card";
+import { GridCard } from "@components/Card";
export default function SettingsAdvancedRoute() {
const [send] = useJsonRpc();
@@ -152,6 +152,52 @@ export default function SettingsAdvancedRoute() {
/>
+
+ {
+ settings.setDebugMode(e.target.checked);
+ }}
+ />
+
+
+ {settings.debugMode && (
+ <>
+
+ handleUsbEmulationToggle(!usbEmulationEnabled)}
+ />
+
+
+
+ {
+ handleResetConfig();
+ window.location.reload();
+ }}
+ />
+
+
+ >
+ )}
+
)}
- {settings.developerMode && (
-
+ {isOnDevice && settings.developerMode && (
+
+
+
+
setCloudUrl(e.target.value)}
+ placeholder="https://api.jetkvm.com"
+ />
+
+
+ handleCloudUrlChange(cloudUrl)}
+ />
+
+
+
+ )}
+ {isOnDevice && settings.developerMode && (
+
- {isOnDevice && (
-
-
-
-
setCloudUrl(e.target.value)}
- placeholder="https://api.jetkvm.com"
- />
-
-
- handleCloudUrlChange(cloudUrl)}
- />
-
-
-
- )}
)}
-
- {
- settings.setDebugMode(e.target.checked);
- }}
- />
-
-
- {settings.debugMode && (
- <>
-
- handleUsbEmulationToggle(!usbEmulationEnabled)}
- />
-
- >
- )}
- {settings.debugMode && (
-
- {
- handleResetConfig();
- window.location.reload();
- }}
- />
-
- )}
);
diff --git a/ui/src/routes/devices.$id.settings.general._index.tsx b/ui/src/routes/devices.$id.settings.general._index.tsx
index 3ad3756..7c7b61c 100644
--- a/ui/src/routes/devices.$id.settings.general._index.tsx
+++ b/ui/src/routes/devices.$id.settings.general._index.tsx
@@ -169,101 +169,105 @@ export default function SettingsGeneralRoute() {