mirror of https://github.com/jetkvm/kvm.git
feat(ui): Add Ctrl+Alt+Del to the action bar (#498)
Since this is the sort of thing we do all the time, make it one-click away
This commit is contained in:
parent
55d7f22c47
commit
1f7c5c94d8
|
@ -1,6 +1,6 @@
|
||||||
import { MdOutlineContentPasteGo } from "react-icons/md";
|
import { MdOutlineContentPasteGo } from "react-icons/md";
|
||||||
import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu";
|
import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu";
|
||||||
import { FaKeyboard } from "react-icons/fa6";
|
import { FaKeyboard, FaLock} from "react-icons/fa6";
|
||||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||||
import { Fragment, useCallback, useRef } from "react";
|
import { Fragment, useCallback, useRef } from "react";
|
||||||
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
||||||
|
@ -19,6 +19,8 @@ import WakeOnLanModal from "@/components/popovers/WakeOnLan/Index";
|
||||||
import MountPopopover from "@/components/popovers/MountPopover";
|
import MountPopopover from "@/components/popovers/MountPopover";
|
||||||
import ExtensionPopover from "@/components/popovers/ExtensionPopover";
|
import ExtensionPopover from "@/components/popovers/ExtensionPopover";
|
||||||
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
|
||||||
|
import useKeyboard from "@/hooks/useKeyboard";
|
||||||
|
import { keys, modifiers } from "@/keyboardMappings";
|
||||||
|
|
||||||
export default function Actionbar({
|
export default function Actionbar({
|
||||||
requestFullscreen,
|
requestFullscreen,
|
||||||
|
@ -56,6 +58,8 @@ export default function Actionbar({
|
||||||
[setDisableFocusTrap],
|
[setDisableFocusTrap],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { sendKeyboardEvent, resetKeyboardState } = useKeyboard();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
||||||
<div
|
<div
|
||||||
|
@ -262,7 +266,23 @@ export default function Actionbar({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{useSettingsStore().actionBarCtrlAltDel && (
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
<Button
|
||||||
|
size="XS"
|
||||||
|
theme="light"
|
||||||
|
text="Ctrl + Alt + Del"
|
||||||
|
LeadingIcon={FaLock}
|
||||||
|
onClick={() => {
|
||||||
|
sendKeyboardEvent(
|
||||||
|
[keys["Delete"]],
|
||||||
|
[modifiers["ControlLeft"], modifiers["AltLeft"]],
|
||||||
|
);
|
||||||
|
setTimeout(resetKeyboardState, 100);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size="XS"
|
size="XS"
|
||||||
|
|
|
@ -308,6 +308,9 @@ interface SettingsState {
|
||||||
keyboardLayout: string;
|
keyboardLayout: string;
|
||||||
setKeyboardLayout: (layout: string) => void;
|
setKeyboardLayout: (layout: string) => void;
|
||||||
|
|
||||||
|
actionBarCtrlAltDel: boolean;
|
||||||
|
setActionBarCtrlAltDel: (enabled: boolean) => void;
|
||||||
|
|
||||||
keyboardLedSync: KeyboardLedSync;
|
keyboardLedSync: KeyboardLedSync;
|
||||||
setKeyboardLedSync: (sync: KeyboardLedSync) => void;
|
setKeyboardLedSync: (sync: KeyboardLedSync) => void;
|
||||||
|
|
||||||
|
@ -345,6 +348,9 @@ export const useSettingsStore = create(
|
||||||
keyboardLayout: "en-US",
|
keyboardLayout: "en-US",
|
||||||
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
|
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
|
||||||
|
|
||||||
|
actionBarCtrlAltDel: false,
|
||||||
|
setActionBarCtrlAltDel: enabled => set({ actionBarCtrlAltDel: enabled }),
|
||||||
|
|
||||||
keyboardLedSync: "auto",
|
keyboardLedSync: "auto",
|
||||||
setKeyboardLedSync: sync => set({ keyboardLedSync: sync }),
|
setKeyboardLedSync: sync => set({ keyboardLedSync: sync }),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
|
|
||||||
|
import { Checkbox } from "@/components/Checkbox";
|
||||||
|
import { SettingsPageHeader } from "@/components/SettingsPageheader";
|
||||||
|
|
||||||
|
import { useSettingsStore } from "@/hooks/stores";
|
||||||
|
|
||||||
|
export default function SettingsCtrlAltDelRoute() {
|
||||||
|
const enableCtrlAltDel = useSettingsStore(state => state.actionBarCtrlAltDel);
|
||||||
|
const setEnableCtrlAltDel = useSettingsStore(state => state.setActionBarCtrlAltDel);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsPageHeader
|
||||||
|
title="Action Bar"
|
||||||
|
description="Customize the action bar of your JetKVM interface"
|
||||||
|
/>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem title="Enable Ctrl-Alt-Del" description="Enable the Ctrl-Alt-Del key on the virtual keyboard">
|
||||||
|
<Checkbox
|
||||||
|
checked={enableCtrlAltDel}
|
||||||
|
onChange={e => setEnableCtrlAltDel(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
import { useEffect } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import { SettingsItem } from "@routes/devices.$id.settings";
|
import { SettingsItem } from "@routes/devices.$id.settings";
|
||||||
import { BacklightSettings, useSettingsStore } from "@/hooks/stores";
|
import { BacklightSettings, useSettingsStore } from "@/hooks/stores";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
|
import Checkbox from "@components/Checkbox";
|
||||||
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
import { UsbDeviceSetting } from "@components/UsbDeviceSetting";
|
import { UsbDeviceSetting } from "@components/UsbDeviceSetting";
|
||||||
|
|
||||||
|
@ -11,6 +12,14 @@ import notifications from "../notifications";
|
||||||
import { UsbInfoSetting } from "../components/UsbInfoSetting";
|
import { UsbInfoSetting } from "../components/UsbInfoSetting";
|
||||||
import { FeatureFlag } from "../components/FeatureFlag";
|
import { FeatureFlag } from "../components/FeatureFlag";
|
||||||
|
|
||||||
|
export interface ActionBarConfig {
|
||||||
|
ctrlAltDel: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultActionBarConfig: ActionBarConfig = {
|
||||||
|
ctrlAltDel: false,
|
||||||
|
};
|
||||||
|
|
||||||
export default function SettingsHardwareRoute() {
|
export default function SettingsHardwareRoute() {
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
|
@ -71,6 +80,18 @@ export default function SettingsHardwareRoute() {
|
||||||
});
|
});
|
||||||
}, [send, setBacklightSettings]);
|
}, [send, setBacklightSettings]);
|
||||||
|
|
||||||
|
const [actionBarConfig, setActionBarConfig] = useState<ActionBarConfig>(defaultActionBarConfig);
|
||||||
|
|
||||||
|
const onActionBarItemChange = useCallback(
|
||||||
|
(key: keyof ActionBarConfig) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setActionBarConfig(prev => ({
|
||||||
|
...prev,
|
||||||
|
[key]: e.target.checked,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader
|
||||||
|
@ -116,6 +137,15 @@ export default function SettingsHardwareRoute() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
<SettingsItem
|
||||||
|
title="Enable Ctrl+Alt+Del Action Bar"
|
||||||
|
description="Enable or disable the action bar action for sending a Ctrl+Alt+Del to the host"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={actionBarConfig.ctrlAltDel}
|
||||||
|
onChange={onActionBarItemChange("ctrlAltDel")}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
{settings.backlightSettings.max_brightness != 0 && (
|
{settings.backlightSettings.max_brightness != 0 && (
|
||||||
<>
|
<>
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
|
|
Loading…
Reference in New Issue