mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-11 05:35:25 +03:00
motion tuner fixes and tweaks
This commit is contained in:
parent
36a3a0a478
commit
1ba4502b07
@ -248,7 +248,10 @@ export default function MotionMaskEditPane({
|
|||||||
<Separator className="my-3 bg-secondary" />
|
<Separator className="my-3 bg-secondary" />
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6 flex flex-col flex-1"
|
||||||
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="polygon.name"
|
name="polygon.name"
|
||||||
@ -267,25 +270,27 @@ export default function MotionMaskEditPane({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
<div className="flex flex-col flex-1 justify-end">
|
||||||
<Button className="flex flex-1" onClick={onCancel}>
|
<div className="flex flex-row gap-2 pt-5">
|
||||||
Cancel
|
<Button className="flex flex-1" onClick={onCancel}>
|
||||||
</Button>
|
Cancel
|
||||||
<Button
|
</Button>
|
||||||
variant="select"
|
<Button
|
||||||
disabled={isLoading}
|
variant="select"
|
||||||
className="flex flex-1"
|
disabled={isLoading}
|
||||||
type="submit"
|
className="flex flex-1"
|
||||||
>
|
type="submit"
|
||||||
{isLoading ? (
|
>
|
||||||
<div className="flex flex-row items-center gap-2">
|
{isLoading ? (
|
||||||
<ActivityIndicator />
|
<div className="flex flex-row items-center gap-2">
|
||||||
<span>Saving...</span>
|
<ActivityIndicator />
|
||||||
</div>
|
<span>Saving...</span>
|
||||||
) : (
|
</div>
|
||||||
"Save"
|
) : (
|
||||||
)}
|
"Save"
|
||||||
</Button>
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -27,6 +27,9 @@ import { Button } from "../ui/button";
|
|||||||
import { Switch } from "../ui/switch";
|
import { Switch } from "../ui/switch";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { Separator } from "../ui/separator";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { LuExternalLink } from "react-icons/lu";
|
||||||
|
|
||||||
type MotionTunerProps = {
|
type MotionTunerProps = {
|
||||||
selectedCamera: string;
|
selectedCamera: string;
|
||||||
@ -45,9 +48,6 @@ export default function MotionTuner({ selectedCamera }: MotionTunerProps) {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
|
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
|
||||||
|
|
||||||
// const [selectedCamera, setSelectedCamera] = useState(cameras[0]?.name);
|
|
||||||
const [nextSelectedCamera, setNextSelectedCamera] = useState("");
|
|
||||||
|
|
||||||
const { send: sendMotionThreshold } = useMotionThreshold(selectedCamera);
|
const { send: sendMotionThreshold } = useMotionThreshold(selectedCamera);
|
||||||
const { send: sendMotionContourArea } = useMotionContourArea(selectedCamera);
|
const { send: sendMotionContourArea } = useMotionContourArea(selectedCamera);
|
||||||
const { send: sendImproveContrast } = useImproveContrast(selectedCamera);
|
const { send: sendImproveContrast } = useImproveContrast(selectedCamera);
|
||||||
@ -58,6 +58,12 @@ export default function MotionTuner({ selectedCamera }: MotionTunerProps) {
|
|||||||
improve_contrast: undefined,
|
improve_contrast: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [origMotionSettings, setOrigMotionSettings] = useState<MotionSettings>({
|
||||||
|
threshold: undefined,
|
||||||
|
contour_area: undefined,
|
||||||
|
improve_contrast: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const cameraConfig = useMemo(() => {
|
const cameraConfig = useMemo(() => {
|
||||||
if (config && selectedCamera) {
|
if (config && selectedCamera) {
|
||||||
return config.cameras[selectedCamera];
|
return config.cameras[selectedCamera];
|
||||||
@ -71,6 +77,11 @@ export default function MotionTuner({ selectedCamera }: MotionTunerProps) {
|
|||||||
contour_area: cameraConfig.motion.contour_area,
|
contour_area: cameraConfig.motion.contour_area,
|
||||||
improve_contrast: cameraConfig.motion.improve_contrast,
|
improve_contrast: cameraConfig.motion.improve_contrast,
|
||||||
});
|
});
|
||||||
|
setOrigMotionSettings({
|
||||||
|
threshold: cameraConfig.motion.threshold,
|
||||||
|
contour_area: cameraConfig.motion.contour_area,
|
||||||
|
improve_contrast: cameraConfig.motion.improve_contrast,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [cameraConfig]);
|
}, [cameraConfig]);
|
||||||
|
|
||||||
@ -150,15 +161,16 @@ export default function MotionTuner({ selectedCamera }: MotionTunerProps) {
|
|||||||
selectedCamera,
|
selectedCamera,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onCancel = useCallback(() => {}, []);
|
const onCancel = useCallback(() => {
|
||||||
|
setMotionSettings(origMotionSettings);
|
||||||
|
setChangedValue(false);
|
||||||
|
}, [origMotionSettings]);
|
||||||
|
|
||||||
const handleDialog = useCallback(
|
const handleDialog = useCallback(
|
||||||
(save: boolean) => {
|
(save: boolean) => {
|
||||||
if (save) {
|
if (save) {
|
||||||
saveToConfig();
|
saveToConfig();
|
||||||
}
|
}
|
||||||
// setSelectedCamera(nextSelectedCamera);
|
|
||||||
setNextSelectedCamera("");
|
|
||||||
setConfirmationDialogOpen(false);
|
setConfirmationDialogOpen(false);
|
||||||
setChangedValue(false);
|
setChangedValue(false);
|
||||||
},
|
},
|
||||||
@ -176,72 +188,117 @@ export default function MotionTuner({ selectedCamera }: MotionTunerProps) {
|
|||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
Motion Detection Tuner
|
Motion Detection Tuner
|
||||||
</Heading>
|
</Heading>
|
||||||
|
<div className="text-sm text-muted-foreground my-3 space-y-3">
|
||||||
|
<p>
|
||||||
|
Frigate uses motion detection as a first line check to see if there
|
||||||
|
is anything happening in the frame worth checking with object
|
||||||
|
detection.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col w-full space-y-10">
|
<div className="flex items-center text-primary">
|
||||||
<div className="flex flex-row mb-5">
|
<Link
|
||||||
<Slider
|
to="https://docs.frigate.video/configuration/motion_detection"
|
||||||
id="motion-threshold"
|
target="_blank"
|
||||||
className="w-[300px]"
|
rel="noopener noreferrer"
|
||||||
disabled={motionSettings.threshold === undefined}
|
className="inline"
|
||||||
value={[motionSettings.threshold ?? 0]}
|
>
|
||||||
min={10}
|
Read the Motion Tuning Guide{" "}
|
||||||
max={80}
|
<LuExternalLink className="size-3 ml-2 inline-flex" />
|
||||||
step={1}
|
</Link>
|
||||||
onValueChange={(value) => {
|
|
||||||
handleMotionConfigChange({ threshold: value[0] });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="motion-threshold" className="px-2">
|
|
||||||
Threshold: {motionSettings.threshold}
|
|
||||||
</Label>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row">
|
</div>
|
||||||
<Slider
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
id="motion-contour-area"
|
<div className="flex flex-col w-full space-y-6">
|
||||||
className="w-[300px]"
|
<div className="mt-2 space-y-6">
|
||||||
disabled={motionSettings.contour_area === undefined}
|
<div className="space-y-0.5">
|
||||||
value={[motionSettings.contour_area ?? 0]}
|
<Label htmlFor="motion-threshold" className="text-md">
|
||||||
min={10}
|
Threshold
|
||||||
max={200}
|
</Label>
|
||||||
step={5}
|
<div className="text-sm text-muted-foreground my-2">
|
||||||
onValueChange={(value) => {
|
<p>
|
||||||
handleMotionConfigChange({ contour_area: value[0] });
|
The threshold value dictates how much of a change in a pixel's
|
||||||
}}
|
luminance is required to be considered motion.{" "}
|
||||||
/>
|
<em>Default: 30</em>
|
||||||
<Label htmlFor="motion-contour-area" className="px-2">
|
</p>
|
||||||
Contour Area: {motionSettings.contour_area}
|
</div>
|
||||||
</Label>
|
</div>
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<Slider
|
||||||
|
id="motion-threshold"
|
||||||
|
className="w-full"
|
||||||
|
disabled={motionSettings.threshold === undefined}
|
||||||
|
value={[motionSettings.threshold ?? 0]}
|
||||||
|
min={5}
|
||||||
|
max={80}
|
||||||
|
step={1}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
handleMotionConfigChange({ threshold: value[0] });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-lg ml-6 mr-2 flex align-center">
|
||||||
|
{motionSettings.threshold}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row">
|
<div className="mt-2 space-y-6">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="motion-threshold" className="text-md">
|
||||||
|
Contour Area
|
||||||
|
</Label>
|
||||||
|
<div className="text-sm text-muted-foreground my-2">
|
||||||
|
<p>
|
||||||
|
The contour area value is used to decide which groups of
|
||||||
|
changed pixels qualify as motion. <em>Default: 10</em>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<Slider
|
||||||
|
id="motion-contour-area"
|
||||||
|
className="w-full"
|
||||||
|
disabled={motionSettings.contour_area === undefined}
|
||||||
|
value={[motionSettings.contour_area ?? 0]}
|
||||||
|
min={5}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
handleMotionConfigChange({ contour_area: value[0] });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="text-lg ml-6 mr-2 flex align-center">
|
||||||
|
{motionSettings.contour_area}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
|
<div className="flex flex-row items-center justify-between">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="improve-contrast">Improve Contrast</Label>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
Improve contrast for darker scenes. <em>Default: ON</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
id="improve-contrast"
|
id="improve-contrast"
|
||||||
|
className="ml-3"
|
||||||
disabled={motionSettings.improve_contrast === undefined}
|
disabled={motionSettings.improve_contrast === undefined}
|
||||||
checked={motionSettings.improve_contrast === true}
|
checked={motionSettings.improve_contrast === true}
|
||||||
onCheckedChange={(isChecked) => {
|
onCheckedChange={(isChecked) => {
|
||||||
handleMotionConfigChange({ improve_contrast: isChecked });
|
handleMotionConfigChange({ improve_contrast: isChecked });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="improve-contrast">Improve Contrast</Label>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center justify-between rounded-lg border p-4">
|
</div>
|
||||||
<div className="space-y-0.5">
|
<div className="flex flex-col flex-1 justify-end">
|
||||||
<div className="text-base">Improve Contrast</div>
|
|
||||||
<div>Improve contrast for darker scenes.</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div checked={field.value} onCheckedChange={field.onChange} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
<div className="flex flex-row gap-2 pt-5">
|
||||||
<Button className="flex flex-1" onClick={onCancel}>
|
<Button className="flex flex-1" onClick={onCancel}>
|
||||||
Cancel
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={isLoading}
|
disabled={!changedValue || isLoading}
|
||||||
className="flex flex-1"
|
className="flex flex-1"
|
||||||
type="submit"
|
onClick={saveToConfig}
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
|||||||
@ -325,70 +325,77 @@ export default function ObjectMaskEditPane({
|
|||||||
<Separator className="my-3 bg-secondary" />
|
<Separator className="my-3 bg-secondary" />
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<form
|
||||||
<FormField
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
control={form.control}
|
className="space-y-6 flex flex-col flex-1"
|
||||||
name="polygon.name"
|
>
|
||||||
render={() => (
|
<div>
|
||||||
<FormItem>
|
<FormField
|
||||||
<FormMessage />
|
control={form.control}
|
||||||
</FormItem>
|
name="polygon.name"
|
||||||
)}
|
render={() => (
|
||||||
/>
|
<FormItem>
|
||||||
<FormField
|
<FormMessage />
|
||||||
control={form.control}
|
</FormItem>
|
||||||
name="objects"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Objects</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
defaultValue={field.value}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select an object type" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
<ZoneObjectSelector camera={polygon.camera} />
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormDescription>
|
|
||||||
The object type that that applies to this object mask.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="polygon.isFinished"
|
|
||||||
render={() => (
|
|
||||||
<FormItem>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
|
||||||
<Button className="flex flex-1" onClick={onCancel}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="select"
|
|
||||||
disabled={isLoading}
|
|
||||||
className="flex flex-1"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<ActivityIndicator />
|
|
||||||
<span>Saving...</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
"Save"
|
|
||||||
)}
|
)}
|
||||||
</Button>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="objects"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Objects</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select an object type" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<ZoneObjectSelector camera={polygon.camera} />
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormDescription>
|
||||||
|
The object type that that applies to this object mask.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="polygon.isFinished"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col flex-1 justify-end">
|
||||||
|
<div className="flex flex-row gap-2 pt-5">
|
||||||
|
<Button className="flex flex-1" onClick={onCancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="select"
|
||||||
|
disabled={isLoading}
|
||||||
|
className="flex flex-1"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row items-center gap-2">
|
||||||
|
<ActivityIndicator />
|
||||||
|
<span>Saving...</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
"Save"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -394,7 +394,7 @@ export default function ZoneEditPane({
|
|||||||
<Separator className="my-3 bg-secondary" />
|
<Separator className="my-3 bg-secondary" />
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="mt-2 space-y-6">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
@ -416,9 +416,7 @@ export default function ZoneEditPane({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex my-2">
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
<Separator className="bg-secondary" />
|
|
||||||
</div>
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="inertia"
|
name="inertia"
|
||||||
@ -440,9 +438,7 @@ export default function ZoneEditPane({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex my-2">
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
<Separator className="bg-secondary" />
|
|
||||||
</div>
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="loitering_time"
|
name="loitering_time"
|
||||||
@ -464,9 +460,7 @@ export default function ZoneEditPane({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex my-2">
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
<Separator className="bg-secondary" />
|
|
||||||
</div>
|
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Objects</FormLabel>
|
<FormLabel>Objects</FormLabel>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
@ -490,14 +484,14 @@ export default function ZoneEditPane({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<div className="flex my-2">
|
|
||||||
<Separator className="bg-secondary" />
|
<Separator className="flex my-2 bg-secondary" />
|
||||||
</div>
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="review_alerts"
|
name="review_alerts"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
<FormItem className="flex flex-row items-center justify-between">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<FormLabel>Alerts</FormLabel>
|
<FormLabel>Alerts</FormLabel>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
@ -509,6 +503,7 @@ export default function ZoneEditPane({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
|
className="ml-3"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@ -518,7 +513,7 @@ export default function ZoneEditPane({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="review_detections"
|
name="review_detections"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
<FormItem className="flex flex-row items-center justify-between">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<FormLabel className="text-base">Detections</FormLabel>
|
<FormLabel className="text-base">Detections</FormLabel>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
@ -530,6 +525,7 @@ export default function ZoneEditPane({
|
|||||||
<Switch
|
<Switch
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
|
className="ml-3"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const Slider = React.forwardRef<
|
|||||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||||
</SliderPrimitive.Track>
|
</SliderPrimitive.Track>
|
||||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full cursor-pointer border-2 border-primary bg-primary ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||||
</SliderPrimitive.Root>
|
</SliderPrimitive.Root>
|
||||||
));
|
));
|
||||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user