mirror of https://github.com/jetkvm/kvm.git
Compare commits
7 Commits
69b3585bae
...
0feb5a9c77
| Author | SHA1 | Date |
|---|---|---|
|
|
0feb5a9c77 | |
|
|
77ffdb4362 | |
|
|
af07dc4997 | |
|
|
ae317fdc5a | |
|
|
fb2ab7db17 | |
|
|
61b99f07ce | |
|
|
041f42a792 |
|
|
@ -118,10 +118,11 @@ 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: 20,
|
InactivityLimitSeconds: 60,
|
||||||
JitterPercentage: 0,
|
JitterPercentage: 25,
|
||||||
ScheduleCronTab: "*/20 * * * * *",
|
ScheduleCronTab: "0 * * * * *",
|
||||||
},
|
},
|
||||||
TLSMode: "",
|
TLSMode: "",
|
||||||
UsbConfig: &usbgadget.Config{
|
UsbConfig: &usbgadget.Config{
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func removeExistingCrobJobs(s gocron.Scheduler) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func initJiggler() {
|
||||||
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="my-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
<span className="mb-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-medium leading-snug text-black dark:text-white">
|
<span className="font-display text-[13px] font-semibold leading-snug text-black dark:text-white">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{description && (
|
{description && (
|
||||||
<span className="my-0.5 text-[13px] font-normal text-slate-600 dark:text-slate-400">
|
<span className="mb-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?: string | null;
|
description?: React.ReactNode | string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(
|
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { 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;
|
||||||
|
|
@ -15,205 +11,84 @@ export interface JigglerConfig {
|
||||||
schedule_cron_tab: string;
|
schedule_cron_tab: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jigglerCrontabConfigs = [
|
export function JigglerSetting({
|
||||||
{
|
onSave,
|
||||||
label: "Every 20 seconds",
|
}: {
|
||||||
value: "*/20 * * * * *",
|
onSave: (jigglerConfig: JigglerConfig) => void;
|
||||||
},
|
}) {
|
||||||
{
|
|
||||||
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 * * * * *",
|
||||||
});
|
});
|
||||||
|
|
||||||
const syncJigglerConfig = useCallback(() => {
|
|
||||||
send("getJigglerConfig", {}, resp => {
|
|
||||||
if ("error" in resp) return;
|
|
||||||
const result = resp.result as JigglerConfig;
|
|
||||||
setJigglerConfigState(result);
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<div className="">
|
<div className="space-y-2">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid max-w-sm grid-cols-1 items-end gap-y-2">
|
||||||
<SelectMenuBasic
|
<InputFieldWithLabel
|
||||||
|
required
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Schedule"
|
label="Cron Schedule"
|
||||||
className="max-w-[192px]"
|
description={
|
||||||
value={scheduleCronTab}
|
<span>
|
||||||
fullWidth
|
Generate with{" "}
|
||||||
onChange={e => {
|
<ExtLink className="text-blue-700 underline" href="https://crontab.guru/">
|
||||||
setScheduleCronTab(e.target.value);
|
crontab.guru
|
||||||
if (e.target.value != "custom") {
|
</ExtLink>
|
||||||
handleJigglerScheduleCronTabChange(e.target.value);
|
</span>
|
||||||
}
|
}
|
||||||
}}
|
placeholder="*/20 * * * * *"
|
||||||
options={[...jigglerCrontabConfigs, {value: "custom", label: "Custom"}]}
|
defaultValue={jigglerConfigState.schedule_cron_tab}
|
||||||
|
onChange={e =>
|
||||||
|
setJigglerConfigState({
|
||||||
|
...jigglerConfigState,
|
||||||
|
schedule_cron_tab: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{scheduleCronTab === "custom" && (
|
|
||||||
<InputFieldWithLabel
|
<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"
|
||||||
className="max-w-[192px]"
|
description="Seconds of inactivity before triggering a jiggle again"
|
||||||
value={jigglerConfigState.inactivity_limit_seconds}
|
value={jigglerConfigState.inactivity_limit_seconds}
|
||||||
fullWidth
|
type="number"
|
||||||
onChange={e => {
|
min="1"
|
||||||
handleJigglerInactivityLimitSecondsChange(e.target.value);
|
max="100"
|
||||||
}}
|
onChange={e =>
|
||||||
options={[...jigglerInactivityConfigs]}
|
setJigglerConfigState({
|
||||||
|
...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="Update Jiggler Config"
|
text="Save Jiggler Config"
|
||||||
onClick={() => handleJigglerConfigSave(jigglerConfigState)}
|
onClick={() => onSave(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-[32px] pl-3 pr-8 text-[13px]",
|
SM: "h-[36px] 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} as="span" />}
|
{label && <FieldLabel label={label} id={id} />}
|
||||||
<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}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
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 { useEffect, useState } from "react";
|
import { useCallback, 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,15 +7,63 @@ 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);
|
||||||
|
|
@ -24,12 +72,11 @@ 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 [jiggler, setJiggler] = useState(false);
|
|
||||||
|
|
||||||
const scrollThrottling = useSettingsStore(state => state.scrollThrottling);
|
const scrollThrottling = useSettingsStore(state => state.scrollThrottling);
|
||||||
const setScrollThrottling = useSettingsStore(
|
const setScrollThrottling = useSettingsStore(state => state.setScrollThrottling);
|
||||||
state => state.setScrollThrottling,
|
|
||||||
);
|
const [selectedJigglerOption, setSelectedJigglerOption] =
|
||||||
|
useState<JigglerValues | null>(null);
|
||||||
|
|
||||||
const scrollThrottlingOptions = [
|
const scrollThrottlingOptions = [
|
||||||
{ value: "0", label: "Off" },
|
{ value: "0", label: "Off" },
|
||||||
|
|
@ -41,28 +88,85 @@ export default function SettingsMouseRoute() {
|
||||||
|
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
|
|
||||||
useEffect(() => {
|
const syncJigglerSettings = useCallback(() => {
|
||||||
send("getJigglerState", {}, resp => {
|
send("getJigglerState", {}, resp => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
setJiggler(resp.result as boolean);
|
const isEnabled = resp.result as boolean;
|
||||||
});
|
|
||||||
|
|
||||||
send("getJigglerConfig", {}, resp => {
|
// If the jiggler is disabled, set the selected option to "disabled" and nothing else
|
||||||
if ("error" in resp) return;
|
if (!isEnabled) return setSelectedJigglerOption("disabled");
|
||||||
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]);
|
||||||
|
|
||||||
const handleJigglerChange = (enabled: boolean) => {
|
useEffect(() => {
|
||||||
send("setJigglerState", { enabled }, resp => {
|
syncJigglerSettings();
|
||||||
if ("error" in resp) {
|
}, [syncJigglerSettings]);
|
||||||
notifications.error(
|
|
||||||
`Failed to set jiggler state: ${resp.error.data || "Unknown error"}`,
|
const saveJigglerConfig = useCallback(
|
||||||
);
|
(jigglerConfig: JigglerConfig) => {
|
||||||
return;
|
// We assume the jiggler should be set to enabled if the config is being updated
|
||||||
}
|
send("setJigglerState", { enabled: true }, async resp => {
|
||||||
setJiggler(enabled);
|
if ("error" in resp) {
|
||||||
});
|
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 (
|
||||||
|
|
@ -83,39 +187,49 @@ 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"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<SelectMenuBasic
|
||||||
checked={jiggler}
|
size="SM"
|
||||||
onChange={e => handleJigglerChange(e.target.checked)}
|
label=""
|
||||||
|
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>
|
||||||
|
|
||||||
{jiggler && (
|
{selectedJigglerOption === "custom" && (
|
||||||
<>
|
<SettingsNestedSection>
|
||||||
<SettingsSectionHeader
|
<JigglerSetting onSave={saveJigglerConfig} />
|
||||||
title="Jiggler Config"
|
</SettingsNestedSection>
|
||||||
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