mirror of https://github.com/jetkvm/kvm.git
feat: add timezone support to jiggler and fix custom settings persistence
- Add timezone field to JigglerConfig with comprehensive IANA timezone list - Fix custom settings not loading current values - Remove business hours preset, add as examples in custom settings - Improve error handling for invalid cron expressions
This commit is contained in:
parent
9f573200b1
commit
4fa02ed66a
|
@ -123,6 +123,7 @@ var defaultConfig = &Config{
|
||||||
InactivityLimitSeconds: 60,
|
InactivityLimitSeconds: 60,
|
||||||
JitterPercentage: 25,
|
JitterPercentage: 25,
|
||||||
ScheduleCronTab: "0 * * * * *",
|
ScheduleCronTab: "0 * * * * *",
|
||||||
|
Timezone: "UTC",
|
||||||
},
|
},
|
||||||
TLSMode: "",
|
TLSMode: "",
|
||||||
UsbConfig: &usbgadget.Config{
|
UsbConfig: &usbgadget.Config{
|
||||||
|
|
|
@ -12,6 +12,7 @@ type JigglerConfig struct {
|
||||||
InactivityLimitSeconds int `json:"inactivity_limit_seconds"`
|
InactivityLimitSeconds int `json:"inactivity_limit_seconds"`
|
||||||
JitterPercentage int `json:"jitter_percentage"`
|
JitterPercentage int `json:"jitter_percentage"`
|
||||||
ScheduleCronTab string `json:"schedule_cron_tab"`
|
ScheduleCronTab string `json:"schedule_cron_tab"`
|
||||||
|
Timezone string `json:"timezone,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var jigglerEnabled = false
|
var jigglerEnabled = false
|
||||||
|
@ -68,6 +69,12 @@ func initJiggler() {
|
||||||
|
|
||||||
func runJigglerCronTab() error {
|
func runJigglerCronTab() error {
|
||||||
cronTab := config.JigglerConfig.ScheduleCronTab
|
cronTab := config.JigglerConfig.ScheduleCronTab
|
||||||
|
|
||||||
|
// Apply timezone if specified
|
||||||
|
if config.JigglerConfig.Timezone != "" && config.JigglerConfig.Timezone != "UTC" {
|
||||||
|
cronTab = fmt.Sprintf("TZ=%s %s", config.JigglerConfig.Timezone, cronTab)
|
||||||
|
}
|
||||||
|
|
||||||
s, err := gocron.NewScheduler()
|
s, err := gocron.NewScheduler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,44 +1,216 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { LuExternalLink } from "react-icons/lu";
|
||||||
|
|
||||||
import { Button } from "@components/Button";
|
import { Button, LinkButton } from "@components/Button";
|
||||||
|
|
||||||
import { InputFieldWithLabel } from "./InputField";
|
import { InputFieldWithLabel } from "./InputField";
|
||||||
import ExtLink from "./ExtLink";
|
import { SelectMenuBasic } from "./SelectMenuBasic";
|
||||||
|
|
||||||
export interface JigglerConfig {
|
export interface JigglerConfig {
|
||||||
inactivity_limit_seconds: number;
|
inactivity_limit_seconds: number;
|
||||||
jitter_percentage: number;
|
jitter_percentage: number;
|
||||||
schedule_cron_tab: string;
|
schedule_cron_tab: string;
|
||||||
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function JigglerSetting({
|
export function JigglerSetting({
|
||||||
onSave,
|
onSave,
|
||||||
|
defaultJigglerState,
|
||||||
}: {
|
}: {
|
||||||
onSave: (jigglerConfig: JigglerConfig) => void;
|
onSave: (jigglerConfig: JigglerConfig) => void;
|
||||||
|
defaultJigglerState?: JigglerConfig;
|
||||||
}) {
|
}) {
|
||||||
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
|
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>(
|
||||||
inactivity_limit_seconds: 20,
|
defaultJigglerState || {
|
||||||
jitter_percentage: 0,
|
inactivity_limit_seconds: 20,
|
||||||
schedule_cron_tab: "*/20 * * * * *",
|
jitter_percentage: 0,
|
||||||
});
|
schedule_cron_tab: "*/20 * * * * *",
|
||||||
|
timezone: "UTC",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const timezoneOptions = [
|
||||||
|
{ value: "UTC", label: "UTC" },
|
||||||
|
|
||||||
|
// Americas - North America
|
||||||
|
{ value: "America/New_York", label: "Eastern Time (US/Canada)" },
|
||||||
|
{ value: "America/Chicago", label: "Central Time (US/Canada)" },
|
||||||
|
{ value: "America/Denver", label: "Mountain Time (US/Canada)" },
|
||||||
|
{ value: "America/Los_Angeles", label: "Pacific Time (US/Canada)" },
|
||||||
|
{ value: "America/Anchorage", label: "Alaska Time" },
|
||||||
|
{ value: "Pacific/Honolulu", label: "Hawaii Time" },
|
||||||
|
{ value: "America/Phoenix", label: "Arizona (No DST)" },
|
||||||
|
{ value: "America/Toronto", label: "Toronto" },
|
||||||
|
{ value: "America/Vancouver", label: "Vancouver" },
|
||||||
|
{ value: "America/Montreal", label: "Montreal" },
|
||||||
|
{ value: "America/Halifax", label: "Halifax (Atlantic)" },
|
||||||
|
{ value: "America/St_Johns", label: "Newfoundland" },
|
||||||
|
|
||||||
|
// Americas - Central & South America
|
||||||
|
{ value: "America/Mexico_City", label: "Mexico City" },
|
||||||
|
{ value: "America/Cancun", label: "Cancun" },
|
||||||
|
{ value: "America/Guatemala", label: "Guatemala" },
|
||||||
|
{ value: "America/Costa_Rica", label: "Costa Rica" },
|
||||||
|
{ value: "America/Panama", label: "Panama" },
|
||||||
|
{ value: "America/Bogota", label: "Bogotá" },
|
||||||
|
{ value: "America/Lima", label: "Lima" },
|
||||||
|
{ value: "America/Santiago", label: "Santiago" },
|
||||||
|
{ value: "America/Buenos_Aires", label: "Buenos Aires" },
|
||||||
|
{ value: "America/Montevideo", label: "Montevideo" },
|
||||||
|
{ value: "America/Sao_Paulo", label: "São Paulo" },
|
||||||
|
{ value: "America/Rio_Branco", label: "Rio de Janeiro" },
|
||||||
|
{ value: "America/Caracas", label: "Caracas" },
|
||||||
|
{ value: "America/La_Paz", label: "La Paz" },
|
||||||
|
|
||||||
|
// Europe - Western Europe
|
||||||
|
{ value: "Europe/London", label: "London (GMT/BST)" },
|
||||||
|
{ value: "Europe/Dublin", label: "Dublin (GMT/IST)" },
|
||||||
|
{ value: "Europe/Lisbon", label: "Lisbon (WET/WEST)" },
|
||||||
|
{ value: "Atlantic/Reykjavik", label: "Reykjavik (GMT)" },
|
||||||
|
|
||||||
|
// Europe - Central Europe
|
||||||
|
{ value: "Europe/Paris", label: "Paris (CET/CEST)" },
|
||||||
|
{ value: "Europe/Berlin", label: "Berlin (CET/CEST)" },
|
||||||
|
{ value: "Europe/Rome", label: "Rome (CET/CEST)" },
|
||||||
|
{ value: "Europe/Madrid", label: "Madrid (CET/CEST)" },
|
||||||
|
{ value: "Europe/Amsterdam", label: "Amsterdam (CET/CEST)" },
|
||||||
|
{ value: "Europe/Brussels", label: "Brussels (CET/CEST)" },
|
||||||
|
{ value: "Europe/Vienna", label: "Vienna (CET/CEST)" },
|
||||||
|
{ value: "Europe/Zurich", label: "Zurich (CET/CEST)" },
|
||||||
|
{ value: "Europe/Prague", label: "Prague (CET/CEST)" },
|
||||||
|
{ value: "Europe/Warsaw", label: "Warsaw (CET/CEST)" },
|
||||||
|
{ value: "Europe/Budapest", label: "Budapest (CET/CEST)" },
|
||||||
|
{ value: "Europe/Stockholm", label: "Stockholm (CET/CEST)" },
|
||||||
|
{ value: "Europe/Copenhagen", label: "Copenhagen (CET/CEST)" },
|
||||||
|
{ value: "Europe/Oslo", label: "Oslo (CET/CEST)" },
|
||||||
|
{ value: "Europe/Helsinki", label: "Helsinki (EET/EEST)" },
|
||||||
|
|
||||||
|
// Europe - Eastern Europe
|
||||||
|
{ value: "Europe/Athens", label: "Athens (EET/EEST)" },
|
||||||
|
{ value: "Europe/Istanbul", label: "Istanbul (TRT)" },
|
||||||
|
{ value: "Europe/Bucharest", label: "Bucharest (EET/EEST)" },
|
||||||
|
{ value: "Europe/Sofia", label: "Sofia (EET/EEST)" },
|
||||||
|
{ value: "Europe/Kiev", label: "Kiev (EET/EEST)" },
|
||||||
|
{ value: "Europe/Moscow", label: "Moscow (MSK)" },
|
||||||
|
{ value: "Europe/Minsk", label: "Minsk (MSK)" },
|
||||||
|
|
||||||
|
// Asia - East Asia
|
||||||
|
{ value: "Asia/Tokyo", label: "Tokyo (JST)" },
|
||||||
|
{ value: "Asia/Seoul", label: "Seoul (KST)" },
|
||||||
|
{ value: "Asia/Shanghai", label: "Shanghai (CST)" },
|
||||||
|
{ value: "Asia/Hong_Kong", label: "Hong Kong (HKT)" },
|
||||||
|
{ value: "Asia/Taipei", label: "Taipei (CST)" },
|
||||||
|
{ value: "Asia/Manila", label: "Manila (PST)" },
|
||||||
|
|
||||||
|
// Asia - Southeast Asia
|
||||||
|
{ value: "Asia/Singapore", label: "Singapore (SGT)" },
|
||||||
|
{ value: "Asia/Bangkok", label: "Bangkok (ICT)" },
|
||||||
|
{ value: "Asia/Ho_Chi_Minh", label: "Ho Chi Minh City (ICT)" },
|
||||||
|
{ value: "Asia/Jakarta", label: "Jakarta (WIB)" },
|
||||||
|
{ value: "Asia/Kuala_Lumpur", label: "Kuala Lumpur (MYT)" },
|
||||||
|
|
||||||
|
// Asia - South Asia
|
||||||
|
{ value: "Asia/Kolkata", label: "India (IST)" },
|
||||||
|
{ value: "Asia/Dhaka", label: "Dhaka (BST)" },
|
||||||
|
{ value: "Asia/Karachi", label: "Karachi (PKT)" },
|
||||||
|
{ value: "Asia/Colombo", label: "Colombo (IST)" },
|
||||||
|
{ value: "Asia/Kathmandu", label: "Kathmandu (NPT)" },
|
||||||
|
|
||||||
|
// Asia - Central Asia
|
||||||
|
{ value: "Asia/Tashkent", label: "Tashkent (UZT)" },
|
||||||
|
{ value: "Asia/Almaty", label: "Almaty (ALMT)" },
|
||||||
|
{ value: "Asia/Yekaterinburg", label: "Yekaterinburg (YEKT)" },
|
||||||
|
{ value: "Asia/Novosibirsk", label: "Novosibirsk (NOVT)" },
|
||||||
|
|
||||||
|
// Asia - Middle East
|
||||||
|
{ value: "Asia/Dubai", label: "Dubai (GST)" },
|
||||||
|
{ value: "Asia/Qatar", label: "Doha (AST)" },
|
||||||
|
{ value: "Asia/Kuwait", label: "Kuwait (AST)" },
|
||||||
|
{ value: "Asia/Riyadh", label: "Riyadh (AST)" },
|
||||||
|
{ value: "Asia/Baghdad", label: "Baghdad (AST)" },
|
||||||
|
{ value: "Asia/Tehran", label: "Tehran (IRST)" },
|
||||||
|
{ value: "Asia/Jerusalem", label: "Jerusalem (IST)" },
|
||||||
|
{ value: "Asia/Beirut", label: "Beirut (EET)" },
|
||||||
|
|
||||||
|
// Africa
|
||||||
|
{ value: "Africa/Cairo", label: "Cairo (EET)" },
|
||||||
|
{ value: "Africa/Lagos", label: "Lagos (WAT)" },
|
||||||
|
{ value: "Africa/Nairobi", label: "Nairobi (EAT)" },
|
||||||
|
{ value: "Africa/Johannesburg", label: "Johannesburg (SAST)" },
|
||||||
|
{ value: "Africa/Cape_Town", label: "Cape Town (SAST)" },
|
||||||
|
{ value: "Africa/Casablanca", label: "Casablanca (WET)" },
|
||||||
|
{ value: "Africa/Tunis", label: "Tunis (CET)" },
|
||||||
|
{ value: "Africa/Algiers", label: "Algiers (CET)" },
|
||||||
|
{ value: "Africa/Addis_Ababa", label: "Addis Ababa (EAT)" },
|
||||||
|
|
||||||
|
// Australia/Oceania
|
||||||
|
{ value: "Australia/Sydney", label: "Sydney (AEDT/AEST)" },
|
||||||
|
{ value: "Australia/Melbourne", label: "Melbourne (AEDT/AEST)" },
|
||||||
|
{ value: "Australia/Brisbane", label: "Brisbane (AEST)" },
|
||||||
|
{ value: "Australia/Perth", label: "Perth (AWST)" },
|
||||||
|
{ value: "Australia/Adelaide", label: "Adelaide (ACDT/ACST)" },
|
||||||
|
{ value: "Australia/Darwin", label: "Darwin (ACST)" },
|
||||||
|
{ value: "Pacific/Auckland", label: "Auckland (NZDT/NZST)" },
|
||||||
|
{ value: "Pacific/Fiji", label: "Fiji (FJT)" },
|
||||||
|
{ value: "Pacific/Guam", label: "Guam (ChST)" },
|
||||||
|
{ value: "Pacific/Tahiti", label: "Tahiti (TAHT)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const exampleConfigs = [
|
||||||
|
{
|
||||||
|
name: "Business Hours 9-17",
|
||||||
|
config: {
|
||||||
|
inactivity_limit_seconds: 60,
|
||||||
|
jitter_percentage: 25,
|
||||||
|
schedule_cron_tab: "0 * 9-17 * * 1-5",
|
||||||
|
timezone: jigglerConfigState.timezone || "UTC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Business Hours 8-17",
|
||||||
|
config: {
|
||||||
|
inactivity_limit_seconds: 60,
|
||||||
|
jitter_percentage: 25,
|
||||||
|
schedule_cron_tab: "0 * 8-17 * * 1-5",
|
||||||
|
timezone: jigglerConfigState.timezone || "UTC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-4">
|
||||||
<div className="grid max-w-sm grid-cols-1 items-end gap-y-2">
|
<div className="space-y-2">
|
||||||
|
<h4 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
Examples
|
||||||
|
</h4>
|
||||||
|
<div className="flex gap-2 flex-wrap">
|
||||||
|
{exampleConfigs.map((example, index) => (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
size="XS"
|
||||||
|
theme="light"
|
||||||
|
text={example.name}
|
||||||
|
onClick={() => setJigglerConfigState(example.config)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<LinkButton
|
||||||
|
to="https://crontab.guru/examples.html"
|
||||||
|
size="XS"
|
||||||
|
theme="light"
|
||||||
|
text="More examples"
|
||||||
|
LeadingIcon={LuExternalLink}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 items-end gap-4">
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
required
|
required
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Cron Schedule"
|
label="Cron Schedule"
|
||||||
description={
|
description="Cron expression for scheduling"
|
||||||
<span>
|
|
||||||
Generate with{" "}
|
|
||||||
<ExtLink className="text-blue-700 underline" href="https://crontab.guru/">
|
|
||||||
crontab.guru
|
|
||||||
</ExtLink>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
placeholder="*/20 * * * * *"
|
placeholder="*/20 * * * * *"
|
||||||
defaultValue={jigglerConfigState.schedule_cron_tab}
|
value={jigglerConfigState.schedule_cron_tab}
|
||||||
onChange={e =>
|
onChange={e =>
|
||||||
setJigglerConfigState({
|
setJigglerConfigState({
|
||||||
...jigglerConfigState,
|
...jigglerConfigState,
|
||||||
|
@ -50,7 +222,7 @@ export function JigglerSetting({
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Inactivity Limit Seconds"
|
label="Inactivity Limit Seconds"
|
||||||
description="Seconds of inactivity before triggering a jiggle again"
|
description="Inactivity time before jiggle"
|
||||||
value={jigglerConfigState.inactivity_limit_seconds}
|
value={jigglerConfigState.inactivity_limit_seconds}
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
|
@ -70,7 +242,7 @@ export function JigglerSetting({
|
||||||
description="To avoid recognizable patterns"
|
description="To avoid recognizable patterns"
|
||||||
placeholder="25"
|
placeholder="25"
|
||||||
TrailingElm={<span className="px-2 text-xs text-slate-500">%</span>}
|
TrailingElm={<span className="px-2 text-xs text-slate-500">%</span>}
|
||||||
defaultValue={jigglerConfigState.jitter_percentage}
|
value={jigglerConfigState.jitter_percentage}
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
|
@ -81,9 +253,23 @@ export function JigglerSetting({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label="Timezone"
|
||||||
|
description="Timezone for cron schedule"
|
||||||
|
value={jigglerConfigState.timezone || "UTC"}
|
||||||
|
onChange={e =>
|
||||||
|
setJigglerConfigState({
|
||||||
|
...jigglerConfigState,
|
||||||
|
timezone: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
options={timezoneOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex gap-x-2">
|
<div className="flex gap-x-2">
|
||||||
<Button
|
<Button
|
||||||
size="SM"
|
size="SM"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface JigglerConfig {
|
||||||
inactivity_limit_seconds: number;
|
inactivity_limit_seconds: number;
|
||||||
jitter_percentage: number;
|
jitter_percentage: number;
|
||||||
schedule_cron_tab: string;
|
schedule_cron_tab: string;
|
||||||
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jigglerOptions = [
|
const jigglerOptions = [
|
||||||
|
@ -52,15 +53,6 @@ const jigglerOptions = [
|
||||||
schedule_cron_tab: "0 */5 * * * *",
|
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;
|
] as const;
|
||||||
|
|
||||||
type JigglerValues = (typeof jigglerOptions)[number]["value"] | "custom";
|
type JigglerValues = (typeof jigglerOptions)[number]["value"] | "custom";
|
||||||
|
@ -77,6 +69,8 @@ export default function SettingsMouseRoute() {
|
||||||
|
|
||||||
const [selectedJigglerOption, setSelectedJigglerOption] =
|
const [selectedJigglerOption, setSelectedJigglerOption] =
|
||||||
useState<JigglerValues | null>(null);
|
useState<JigglerValues | null>(null);
|
||||||
|
const [currentJigglerConfig, setCurrentJigglerConfig] =
|
||||||
|
useState<JigglerConfig | null>(null);
|
||||||
|
|
||||||
const scrollThrottlingOptions = [
|
const scrollThrottlingOptions = [
|
||||||
{ value: "0", label: "Off" },
|
{ value: "0", label: "Off" },
|
||||||
|
@ -99,6 +93,8 @@ export default function SettingsMouseRoute() {
|
||||||
send("getJigglerConfig", {}, resp => {
|
send("getJigglerConfig", {}, resp => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
const result = resp.result as JigglerConfig;
|
const result = resp.result as JigglerConfig;
|
||||||
|
setCurrentJigglerConfig(result);
|
||||||
|
|
||||||
const value = jigglerOptions.find(
|
const value = jigglerOptions.find(
|
||||||
o =>
|
o =>
|
||||||
o?.config?.inactivity_limit_seconds === result.inactivity_limit_seconds &&
|
o?.config?.inactivity_limit_seconds === result.inactivity_limit_seconds &&
|
||||||
|
@ -128,9 +124,16 @@ export default function SettingsMouseRoute() {
|
||||||
|
|
||||||
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
return notifications.error(
|
const errorMsg = resp.error.data || "Unknown error";
|
||||||
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
|
|
||||||
);
|
// Check for cron syntax errors and provide user-friendly message
|
||||||
|
if (errorMsg.includes("invalid syntax") || errorMsg.includes("parse failure") || errorMsg.includes("invalid cron")) {
|
||||||
|
return notifications.error(
|
||||||
|
"Invalid cron expression. Please check your schedule format (e.g., '0 * * * * *' for every minute)."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifications.error(`Failed to set jiggler config: ${errorMsg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications.success(`Jiggler Config successfully updated`);
|
notifications.success(`Jiggler Config successfully updated`);
|
||||||
|
@ -204,7 +207,7 @@ export default function SettingsMouseRoute() {
|
||||||
|
|
||||||
<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"
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
|
@ -222,13 +225,16 @@ export default function SettingsMouseRoute() {
|
||||||
e.target.value as (typeof jigglerOptions)[number]["value"],
|
e.target.value as (typeof jigglerOptions)[number]["value"],
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
fullWidth
|
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
{selectedJigglerOption === "custom" && (
|
{selectedJigglerOption === "custom" && (
|
||||||
<SettingsNestedSection>
|
<SettingsNestedSection>
|
||||||
<JigglerSetting onSave={saveJigglerConfig} />
|
<JigglerSetting
|
||||||
|
onSave={saveJigglerConfig}
|
||||||
|
defaultJigglerState={currentJigglerConfig || undefined}
|
||||||
|
/>
|
||||||
</SettingsNestedSection>
|
</SettingsNestedSection>
|
||||||
)}
|
)}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
Loading…
Reference in New Issue