mirror of https://github.com/jetkvm/kvm.git
Compare commits
No commits in common. "0feb5a9c77eacb0804be74a4e369b016d06e31d2" and "69b3585baebc979ae6dd226b40ddf2c25d080a96" have entirely different histories.
0feb5a9c77
...
69b3585bae
|
|
@ -118,11 +118,10 @@ var defaultConfig = &Config{
|
||||||
DisplayMaxBrightness: 64,
|
DisplayMaxBrightness: 64,
|
||||||
DisplayDimAfterSec: 120, // 2 minutes
|
DisplayDimAfterSec: 120, // 2 minutes
|
||||||
DisplayOffAfterSec: 1800, // 30 minutes
|
DisplayOffAfterSec: 1800, // 30 minutes
|
||||||
// This is the "Standard" jiggler option in the UI
|
|
||||||
JigglerConfig: &JigglerConfig{
|
JigglerConfig: &JigglerConfig{
|
||||||
InactivityLimitSeconds: 60,
|
InactivityLimitSeconds: 20,
|
||||||
JitterPercentage: 25,
|
JitterPercentage: 0,
|
||||||
ScheduleCronTab: "0 * * * * *",
|
ScheduleCronTab: "*/20 * * * * *",
|
||||||
},
|
},
|
||||||
TLSMode: "",
|
TLSMode: "",
|
||||||
UsbConfig: &usbgadget.Config{
|
UsbConfig: &usbgadget.Config{
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func removeExistingCrobJobs(s gocron.Scheduler) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initJiggler() {
|
func init() {
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
err := runJigglerCronTab()
|
err := runJigglerCronTab()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function FieldLabel({
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{description && (
|
{description && (
|
||||||
<span className="mb-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
<span className="my-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
||||||
{description}
|
{description}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -36,11 +36,11 @@ export default function FieldLabel({
|
||||||
} else if (as === "span") {
|
} else if (as === "span") {
|
||||||
return (
|
return (
|
||||||
<div className="flex select-none flex-col">
|
<div className="flex select-none flex-col">
|
||||||
<span className="font-display text-[13px] font-semibold leading-snug text-black dark:text-white">
|
<span className="font-display text-[13px] font-medium leading-snug text-black dark:text-white">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{description && (
|
{description && (
|
||||||
<span className="mb-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
<span className="my-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
||||||
{description}
|
{description}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -49,4 +49,4 @@ export default function FieldLabel({
|
||||||
} else {
|
} else {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ type InputFieldProps = {
|
||||||
|
|
||||||
type InputFieldWithLabelProps = InputFieldProps & {
|
type InputFieldWithLabelProps = InputFieldProps & {
|
||||||
label: React.ReactNode;
|
label: React.ReactNode;
|
||||||
description?: React.ReactNode | string | null;
|
description?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(
|
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
import { useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Button } from "@components/Button";
|
import { Button } from "@components/Button";
|
||||||
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
|
|
||||||
|
import { useJsonRpc } from "../hooks/useJsonRpc";
|
||||||
|
import notifications from "../notifications";
|
||||||
|
|
||||||
import { InputFieldWithLabel } from "./InputField";
|
import { InputFieldWithLabel } from "./InputField";
|
||||||
import ExtLink from "./ExtLink";
|
|
||||||
|
|
||||||
export interface JigglerConfig {
|
export interface JigglerConfig {
|
||||||
inactivity_limit_seconds: number;
|
inactivity_limit_seconds: number;
|
||||||
|
|
@ -11,84 +15,205 @@ export interface JigglerConfig {
|
||||||
schedule_cron_tab: string;
|
schedule_cron_tab: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function JigglerSetting({
|
const jigglerCrontabConfigs = [
|
||||||
onSave,
|
{
|
||||||
}: {
|
label: "Every 20 seconds",
|
||||||
onSave: (jigglerConfig: JigglerConfig) => void;
|
value: "*/20 * * * * *",
|
||||||
}) {
|
},
|
||||||
|
{
|
||||||
|
label: "Every 40 seconds",
|
||||||
|
value: "*/40 * * * * *",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Every 1 minute",
|
||||||
|
value: "0 * * * * *",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Every 3 minutes",
|
||||||
|
value: "0 */3 * * * *",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const jigglerJitterConfigs = [
|
||||||
|
{
|
||||||
|
label: "No Jitter",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "10%",
|
||||||
|
value: "20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "25%",
|
||||||
|
value: "25",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "50%",
|
||||||
|
value: "50",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const jigglerInactivityConfigs = [
|
||||||
|
{
|
||||||
|
label: "20 Seconds",
|
||||||
|
value: "20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "40 Seconds",
|
||||||
|
value: "40",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "1 Minute",
|
||||||
|
value: "60",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "3 Minutes",
|
||||||
|
value: "180",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function JigglerSetting() {
|
||||||
|
const [send] = useJsonRpc();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [jitterPercentage, setJitterPercentage] = useState("");
|
||||||
|
const [scheduleCronTab, setScheduleCronTab] = useState("");
|
||||||
|
|
||||||
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
|
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
|
||||||
inactivity_limit_seconds: 20,
|
inactivity_limit_seconds: 20,
|
||||||
jitter_percentage: 0,
|
jitter_percentage: 0,
|
||||||
schedule_cron_tab: "*/20 * * * * *",
|
schedule_cron_tab: "*/20 * * * * *"
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
const syncJigglerConfig = useCallback(() => {
|
||||||
<div className="space-y-2">
|
send("getJigglerConfig", {}, resp => {
|
||||||
<div className="grid max-w-sm grid-cols-1 items-end gap-y-2">
|
if ("error" in resp) return;
|
||||||
<InputFieldWithLabel
|
const result = resp.result as JigglerConfig;
|
||||||
required
|
setJigglerConfigState(result);
|
||||||
size="SM"
|
|
||||||
label="Cron Schedule"
|
|
||||||
description={
|
|
||||||
<span>
|
|
||||||
Generate with{" "}
|
|
||||||
<ExtLink className="text-blue-700 underline" href="https://crontab.guru/">
|
|
||||||
crontab.guru
|
|
||||||
</ExtLink>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
placeholder="*/20 * * * * *"
|
|
||||||
defaultValue={jigglerConfigState.schedule_cron_tab}
|
|
||||||
onChange={e =>
|
|
||||||
setJigglerConfigState({
|
|
||||||
...jigglerConfigState,
|
|
||||||
schedule_cron_tab: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<InputFieldWithLabel
|
const jitterPercentage = jigglerJitterConfigs.map(u => u.value).includes(result.jitter_percentage.toString())
|
||||||
|
? result.jitter_percentage.toString()
|
||||||
|
: "custom";
|
||||||
|
setJitterPercentage(jitterPercentage)
|
||||||
|
|
||||||
|
const scheduleCronTab = jigglerCrontabConfigs.map(u => u.value).includes(result.schedule_cron_tab)
|
||||||
|
? result.schedule_cron_tab
|
||||||
|
: "custom";
|
||||||
|
setScheduleCronTab(scheduleCronTab)
|
||||||
|
});
|
||||||
|
}, [send]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
syncJigglerConfig()
|
||||||
|
}, [send, syncJigglerConfig]);
|
||||||
|
|
||||||
|
const handleJigglerInactivityLimitSecondsChange = (value: string) => {
|
||||||
|
setJigglerConfigState({ ...jigglerConfigState, inactivity_limit_seconds: Number(value) });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleJigglerJitterPercentageChange = (value: string) => {
|
||||||
|
setJigglerConfigState({ ...jigglerConfigState, jitter_percentage: Number(value) });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleJigglerScheduleCronTabChange = (value: string) => {
|
||||||
|
setJigglerConfigState({ ...jigglerConfigState, schedule_cron_tab: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleJigglerConfigSave = useCallback(
|
||||||
|
(jigglerConfig: JigglerConfig) => {
|
||||||
|
setLoading(true);
|
||||||
|
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
notifications.error(
|
||||||
|
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
notifications.success(
|
||||||
|
`Jiggler Config successfully updated`,
|
||||||
|
);
|
||||||
|
syncJigglerConfig();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[send, syncJigglerConfig],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label="Schedule"
|
||||||
|
className="max-w-[192px]"
|
||||||
|
value={scheduleCronTab}
|
||||||
|
fullWidth
|
||||||
|
onChange={e => {
|
||||||
|
setScheduleCronTab(e.target.value);
|
||||||
|
if (e.target.value != "custom") {
|
||||||
|
handleJigglerScheduleCronTabChange(e.target.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={[...jigglerCrontabConfigs, {value: "custom", label: "Custom"}]}
|
||||||
|
/>
|
||||||
|
{scheduleCronTab === "custom" && (
|
||||||
|
<InputFieldWithLabel
|
||||||
|
required
|
||||||
|
label="Jiggler Crontab"
|
||||||
|
placeholder="*/20 * * * * *"
|
||||||
|
defaultValue={jigglerConfigState.schedule_cron_tab}
|
||||||
|
onChange={e => handleJigglerScheduleCronTabChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label="Jitter Percentage"
|
||||||
|
className="max-w-[192px]"
|
||||||
|
value={jitterPercentage}
|
||||||
|
fullWidth
|
||||||
|
onChange={e => {
|
||||||
|
setJitterPercentage(e.target.value);
|
||||||
|
if (e.target.value != "custom") {
|
||||||
|
handleJigglerJitterPercentageChange(e.target.value)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={[...jigglerJitterConfigs, {value: "custom", label: "Custom"}]}
|
||||||
|
/>
|
||||||
|
{jitterPercentage === "custom" && (
|
||||||
|
<InputFieldWithLabel
|
||||||
|
required
|
||||||
|
label="Jitter Percentage"
|
||||||
|
placeholder="25"
|
||||||
|
defaultValue={jigglerConfigState.jitter_percentage}
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
onChange={e => handleJigglerJitterPercentageChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Inactivity Limit Seconds"
|
label="Inactivity Limit Seconds"
|
||||||
description="Seconds of inactivity before triggering a jiggle again"
|
className="max-w-[192px]"
|
||||||
value={jigglerConfigState.inactivity_limit_seconds}
|
value={jigglerConfigState.inactivity_limit_seconds}
|
||||||
type="number"
|
fullWidth
|
||||||
min="1"
|
onChange={e => {
|
||||||
max="100"
|
handleJigglerInactivityLimitSecondsChange(e.target.value);
|
||||||
onChange={e =>
|
}}
|
||||||
setJigglerConfigState({
|
options={[...jigglerInactivityConfigs]}
|
||||||
...jigglerConfigState,
|
|
||||||
inactivity_limit_seconds: Number(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<InputFieldWithLabel
|
|
||||||
required
|
|
||||||
size="SM"
|
|
||||||
label="Random delay"
|
|
||||||
description="To avoid recognizable patterns"
|
|
||||||
placeholder="25"
|
|
||||||
TrailingElm={<span className="px-2 text-xs text-slate-500">%</span>}
|
|
||||||
defaultValue={jigglerConfigState.jitter_percentage}
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
onChange={e =>
|
|
||||||
setJigglerConfigState({
|
|
||||||
...jigglerConfigState,
|
|
||||||
jitter_percentage: Number(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex gap-x-2">
|
<div className="mt-6 flex gap-x-2">
|
||||||
<Button
|
<Button
|
||||||
|
loading={loading}
|
||||||
size="SM"
|
size="SM"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
text="Save Jiggler Config"
|
text="Update Jiggler Config"
|
||||||
onClick={() => onSave(jigglerConfigState)}
|
onClick={() => handleJigglerConfigSave(jigglerConfigState)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type SelectMenuProps = Pick<
|
||||||
|
|
||||||
const sizes = {
|
const sizes = {
|
||||||
XS: "h-[24.5px] pl-3 pr-8 text-xs",
|
XS: "h-[24.5px] pl-3 pr-8 text-xs",
|
||||||
SM: "h-[36px] pl-3 pr-8 text-[13px]",
|
SM: "h-[32px] pl-3 pr-8 text-[13px]",
|
||||||
MD: "h-[40px] pl-4 pr-10 text-sm",
|
MD: "h-[40px] pl-4 pr-10 text-sm",
|
||||||
LG: "h-[48px] pl-4 pr-10 px-5 text-base",
|
LG: "h-[48px] pl-4 pr-10 px-5 text-base",
|
||||||
};
|
};
|
||||||
|
|
@ -62,7 +62,7 @@ export const SelectMenuBasic = React.forwardRef<HTMLSelectElement, SelectMenuPro
|
||||||
"text-sm",
|
"text-sm",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{label && <FieldLabel label={label} id={id} />}
|
{label && <FieldLabel label={label} id={id} as="span" />}
|
||||||
<Card className="w-auto border! border-solid border-slate-800/30! shadow-xs outline-0 dark:border-slate-300/30!">
|
<Card className="w-auto border! border-solid border-slate-800/30! shadow-xs outline-0 dark:border-slate-300/30!">
|
||||||
<select
|
<select
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
export default function SettingsNestedSection({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="ml-2 border-l border-slate-800/30 pl-4 dark:border-slate-300/30">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { CheckCircleIcon } from "@heroicons/react/16/solid";
|
import { CheckCircleIcon } from "@heroicons/react/16/solid";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import MouseIcon from "@/assets/mouse-icon.svg";
|
import MouseIcon from "@/assets/mouse-icon.svg";
|
||||||
import PointingFinger from "@/assets/pointing-finger.svg";
|
import PointingFinger from "@/assets/pointing-finger.svg";
|
||||||
|
|
@ -7,63 +7,15 @@ import { GridCard } from "@/components/Card";
|
||||||
import { Checkbox } from "@/components/Checkbox";
|
import { Checkbox } from "@/components/Checkbox";
|
||||||
import { useSettingsStore } from "@/hooks/stores";
|
import { useSettingsStore } from "@/hooks/stores";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
|
import notifications from "@/notifications";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
|
import { JigglerSetting } from "@components/JigglerSetting";
|
||||||
|
import { SettingsSectionHeader } from "@components/SettingsSectionHeader";
|
||||||
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
|
|
||||||
import { cx } from "../cva.config";
|
import { cx } from "../cva.config";
|
||||||
|
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
import notifications from "../notifications";
|
|
||||||
import SettingsNestedSection from "../components/SettingsNestedSection";
|
|
||||||
import { JigglerSetting } from "@components/JigglerSetting";
|
|
||||||
|
|
||||||
export interface JigglerConfig {
|
|
||||||
inactivity_limit_seconds: number;
|
|
||||||
jitter_percentage: number;
|
|
||||||
schedule_cron_tab: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jigglerOptions = [
|
|
||||||
{ value: "disabled", label: "Disabled", config: null },
|
|
||||||
{
|
|
||||||
value: "frequent",
|
|
||||||
label: "Frequent - 30s",
|
|
||||||
config: {
|
|
||||||
inactivity_limit_seconds: 30,
|
|
||||||
jitter_percentage: 25,
|
|
||||||
schedule_cron_tab: "*/30 * * * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "standard",
|
|
||||||
label: "Standard - 1m",
|
|
||||||
config: {
|
|
||||||
inactivity_limit_seconds: 60,
|
|
||||||
jitter_percentage: 25,
|
|
||||||
schedule_cron_tab: "0 * * * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "light",
|
|
||||||
label: "Light - 5m",
|
|
||||||
config: {
|
|
||||||
inactivity_limit_seconds: 300,
|
|
||||||
jitter_percentage: 25,
|
|
||||||
schedule_cron_tab: "0 */5 * * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "business_hours",
|
|
||||||
label: "Business Hours - 1m - (Mon-Fri 9-17)",
|
|
||||||
config: {
|
|
||||||
inactivity_limit_seconds: 60,
|
|
||||||
jitter_percentage: 25,
|
|
||||||
schedule_cron_tab: "0 * 9-17 * * 1-5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
type JigglerValues = (typeof jigglerOptions)[number]["value"] | "custom";
|
|
||||||
|
|
||||||
export default function SettingsMouseRoute() {
|
export default function SettingsMouseRoute() {
|
||||||
const hideCursor = useSettingsStore(state => state.isCursorHidden);
|
const hideCursor = useSettingsStore(state => state.isCursorHidden);
|
||||||
|
|
@ -72,11 +24,12 @@ export default function SettingsMouseRoute() {
|
||||||
const mouseMode = useSettingsStore(state => state.mouseMode);
|
const mouseMode = useSettingsStore(state => state.mouseMode);
|
||||||
const setMouseMode = useSettingsStore(state => state.setMouseMode);
|
const setMouseMode = useSettingsStore(state => state.setMouseMode);
|
||||||
|
|
||||||
const scrollThrottling = useSettingsStore(state => state.scrollThrottling);
|
const [jiggler, setJiggler] = useState(false);
|
||||||
const setScrollThrottling = useSettingsStore(state => state.setScrollThrottling);
|
|
||||||
|
|
||||||
const [selectedJigglerOption, setSelectedJigglerOption] =
|
const scrollThrottling = useSettingsStore(state => state.scrollThrottling);
|
||||||
useState<JigglerValues | null>(null);
|
const setScrollThrottling = useSettingsStore(
|
||||||
|
state => state.setScrollThrottling,
|
||||||
|
);
|
||||||
|
|
||||||
const scrollThrottlingOptions = [
|
const scrollThrottlingOptions = [
|
||||||
{ value: "0", label: "Off" },
|
{ value: "0", label: "Off" },
|
||||||
|
|
@ -88,85 +41,28 @@ export default function SettingsMouseRoute() {
|
||||||
|
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
|
|
||||||
const syncJigglerSettings = useCallback(() => {
|
useEffect(() => {
|
||||||
send("getJigglerState", {}, resp => {
|
send("getJigglerState", {}, resp => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
const isEnabled = resp.result as boolean;
|
setJiggler(resp.result as boolean);
|
||||||
|
});
|
||||||
|
|
||||||
// If the jiggler is disabled, set the selected option to "disabled" and nothing else
|
send("getJigglerConfig", {}, resp => {
|
||||||
if (!isEnabled) return setSelectedJigglerOption("disabled");
|
if ("error" in resp) return;
|
||||||
|
setJiggler(resp.result as boolean);
|
||||||
send("getJigglerConfig", {}, resp => {
|
|
||||||
if ("error" in resp) return;
|
|
||||||
const result = resp.result as JigglerConfig;
|
|
||||||
const value = jigglerOptions.find(
|
|
||||||
o =>
|
|
||||||
o?.config?.inactivity_limit_seconds === result.inactivity_limit_seconds &&
|
|
||||||
o?.config?.jitter_percentage === result.jitter_percentage &&
|
|
||||||
o?.config?.schedule_cron_tab === result.schedule_cron_tab,
|
|
||||||
)?.value;
|
|
||||||
|
|
||||||
setSelectedJigglerOption(value || "custom");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, [send]);
|
}, [send]);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleJigglerChange = (enabled: boolean) => {
|
||||||
syncJigglerSettings();
|
send("setJigglerState", { enabled }, resp => {
|
||||||
}, [syncJigglerSettings]);
|
if ("error" in resp) {
|
||||||
|
notifications.error(
|
||||||
const saveJigglerConfig = useCallback(
|
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
|
||||||
(jigglerConfig: JigglerConfig) => {
|
);
|
||||||
// We assume the jiggler should be set to enabled if the config is being updated
|
return;
|
||||||
send("setJigglerState", { enabled: true }, async resp => {
|
}
|
||||||
if ("error" in resp) {
|
setJiggler(enabled);
|
||||||
return notifications.error(
|
});
|
||||||
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
return notifications.error(
|
|
||||||
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifications.success(`Jiggler Config successfully updated`);
|
|
||||||
syncJigglerSettings();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[send, syncJigglerSettings],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleJigglerChange = (option: JigglerValues) => {
|
|
||||||
if (option === "custom") {
|
|
||||||
setSelectedJigglerOption("custom");
|
|
||||||
// We don't need to sync the jiggler settings when the option is "custom". The user will press "Save" to save the custom settings.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to update the device jiggler state when the option is "disabled"
|
|
||||||
if (option === "disabled") {
|
|
||||||
send("setJigglerState", { enabled: false }, async resp => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
return notifications.error(
|
|
||||||
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
notifications.success(`Jiggler Config successfully updated`);
|
|
||||||
return setSelectedJigglerOption("disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
const jigglerConfig = jigglerOptions.find(o => o.value === option)?.config;
|
|
||||||
if (!jigglerConfig) {
|
|
||||||
return notifications.error("There was an error setting the jiggler config");
|
|
||||||
}
|
|
||||||
|
|
||||||
saveJigglerConfig(jigglerConfig);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -187,49 +83,39 @@ export default function SettingsMouseRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Scroll Throttling"
|
title="Scroll Throttling"
|
||||||
description="Reduce the frequency of scroll events"
|
description="Reduce the frequency of scroll events"
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label=""
|
label=""
|
||||||
className="max-w-[292px]"
|
className="max-w-[292px]"
|
||||||
value={scrollThrottling}
|
value={scrollThrottling}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={e => setScrollThrottling(parseInt(e.target.value))}
|
onChange={e => setScrollThrottling(parseInt(e.target.value))}
|
||||||
options={scrollThrottlingOptions}
|
options={scrollThrottlingOptions}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Jiggler"
|
title="Jiggler"
|
||||||
description="Simulate movement of a computer mouse. Prevents sleep mode, standby mode or the screensaver from activating"
|
description="Simulate movement of a computer mouse. Prevents sleep mode, standby mode or the screensaver from activating"
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<Checkbox
|
||||||
size="SM"
|
checked={jiggler}
|
||||||
label=""
|
onChange={e => handleJigglerChange(e.target.checked)}
|
||||||
value={selectedJigglerOption || "disabled"}
|
|
||||||
options={[
|
|
||||||
...jigglerOptions.map(option => ({
|
|
||||||
value: option.value,
|
|
||||||
label: option.label,
|
|
||||||
})),
|
|
||||||
{ value: "custom", label: "Custom" },
|
|
||||||
]}
|
|
||||||
onChange={e => {
|
|
||||||
handleJigglerChange(
|
|
||||||
e.target.value as (typeof jigglerOptions)[number]["value"],
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
{selectedJigglerOption === "custom" && (
|
{jiggler && (
|
||||||
<SettingsNestedSection>
|
<>
|
||||||
<JigglerSetting onSave={saveJigglerConfig} />
|
<SettingsSectionHeader
|
||||||
</SettingsNestedSection>
|
title="Jiggler Config"
|
||||||
|
description="Control the jiggler schedule"
|
||||||
|
/>
|
||||||
|
<JigglerSetting />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsItem title="Modes" description="Choose the mouse input mode" />
|
<SettingsItem title="Modes" description="Choose the mouse input mode" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue