option to show motion only on motion timeline (#10626)

This commit is contained in:
Josh Hawkins 2024-03-23 08:33:50 -05:00 committed by GitHub
parent 8e1d18d06b
commit 4159334520
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 287 additions and 127 deletions

View File

@ -10,10 +10,10 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "../ui/dropdown-menu"; } from "../ui/dropdown-menu";
import { ReviewFilter, ReviewSummary } from "@/types/review"; import { ReviewFilter, ReviewSeverity, ReviewSummary } from "@/types/review";
import { getEndOfDayTimestamp } from "@/utils/dateUtil"; import { getEndOfDayTimestamp } from "@/utils/dateUtil";
import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import { FaCalendarAlt, FaFilter, FaVideo } from "react-icons/fa"; import { FaCalendarAlt, FaFilter, FaRunning, FaVideo } from "react-icons/fa";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
import { Switch } from "../ui/switch"; import { Switch } from "../ui/switch";
@ -27,12 +27,18 @@ type ReviewFilterGroupProps = {
reviewSummary?: ReviewSummary; reviewSummary?: ReviewSummary;
filter?: ReviewFilter; filter?: ReviewFilter;
onUpdateFilter: (filter: ReviewFilter) => void; onUpdateFilter: (filter: ReviewFilter) => void;
severity: ReviewSeverity;
motionOnly: boolean;
setMotionOnly: React.Dispatch<React.SetStateAction<boolean>>;
}; };
export default function ReviewFilterGroup({ export default function ReviewFilterGroup({
reviewSummary, reviewSummary,
filter, filter,
onUpdateFilter, onUpdateFilter,
severity,
motionOnly,
setMotionOnly,
}: ReviewFilterGroupProps) { }: ReviewFilterGroupProps) {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
@ -94,7 +100,7 @@ export default function ReviewFilterGroup({
); );
return ( return (
<div> <div className="flex justify-center">
<CamerasFilterButton <CamerasFilterButton
allCameras={filterValues.cameras} allCameras={filterValues.cameras}
groups={groups} groups={groups}
@ -110,6 +116,12 @@ export default function ReviewFilterGroup({
} }
updateSelectedDay={onUpdateSelectedDay} updateSelectedDay={onUpdateSelectedDay}
/> />
{severity == "significant_motion" ? (
<ShowMotionOnlyButton
motionOnly={motionOnly}
setMotionOnly={setMotionOnly}
/>
) : (
<GeneralFilterButton <GeneralFilterButton
allLabels={filterValues.labels} allLabels={filterValues.labels}
selectedLabels={filter?.labels} selectedLabels={filter?.labels}
@ -121,6 +133,7 @@ export default function ReviewFilterGroup({
onUpdateFilter({ ...filter, showReviewed: reviewed }) onUpdateFilter({ ...filter, showReviewed: reviewed })
} }
/> />
)}
</div> </div>
); );
} }
@ -485,3 +498,46 @@ function GeneralFilterButton({
</Popover> </Popover>
); );
} }
type ShowMotionOnlyButtonProps = {
motionOnly: boolean;
setMotionOnly: React.Dispatch<React.SetStateAction<boolean>>;
};
function ShowMotionOnlyButton({
motionOnly,
setMotionOnly,
}: ShowMotionOnlyButtonProps) {
return (
<>
<div className="hidden md:inline-flex items-center justify-center whitespace-nowrap text-sm bg-secondary text-secondary-foreground h-9 rounded-md md:px-3 md:mx-1">
<Switch
className="ml-1"
id="collapse-motion"
checked={motionOnly}
onCheckedChange={() => {
setMotionOnly(!motionOnly);
}}
/>
<Label
className="mx-2 text-secondary-foreground"
htmlFor="collapse-motion"
>
Motion only
</Label>
</div>
<div className="block md:hidden">
<Button
size="sm"
className="ml-1"
variant="secondary"
onClick={() => setMotionOnly(!motionOnly)}
>
<FaRunning
className={`${motionOnly ? "text-selected" : "text-muted-foreground"}`}
/>
</Button>
</div>
</>
);
}

View File

@ -236,11 +236,13 @@ export function EventReviewTimeline({
const element = selectedTimelineRef.current?.querySelector( const element = selectedTimelineRef.current?.querySelector(
`[data-segment-id="${Math.max(...alignedVisibleTimestamps)}"]`, `[data-segment-id="${Math.max(...alignedVisibleTimestamps)}"]`,
); );
scrollIntoView(element as HTMLDivElement, { if (element) {
scrollIntoView(element, {
scrollMode: "if-needed", scrollMode: "if-needed",
behavior: "smooth", behavior: "smooth",
}); });
} }
}
}, [ }, [
selectedTimelineRef, selectedTimelineRef,
segments, segments,

View File

@ -201,7 +201,7 @@ export function EventSegment({
<div <div
key={segmentKey} key={segmentKey}
data-segment-id={segmentKey} data-segment-id={segmentKey}
className={segmentClasses} className={`segment ${segmentClasses}`}
onClick={segmentClick} onClick={segmentClick}
onTouchEnd={(event) => handleTouchStart(event, segmentClick)} onTouchEnd={(event) => handleTouchStart(event, segmentClick)}
> >

View File

@ -21,6 +21,7 @@ export type MotionReviewTimelineProps = {
showHandlebar?: boolean; showHandlebar?: boolean;
handlebarTime?: number; handlebarTime?: number;
setHandlebarTime?: React.Dispatch<React.SetStateAction<number>>; setHandlebarTime?: React.Dispatch<React.SetStateAction<number>>;
motionOnly?: boolean;
showMinimap?: boolean; showMinimap?: boolean;
minimapStartTime?: number; minimapStartTime?: number;
minimapEndTime?: number; minimapEndTime?: number;
@ -45,6 +46,7 @@ export function MotionReviewTimeline({
showHandlebar = false, showHandlebar = false,
handlebarTime, handlebarTime,
setHandlebarTime, setHandlebarTime,
motionOnly = false,
showMinimap = false, showMinimap = false,
minimapStartTime, minimapStartTime,
minimapEndTime, minimapEndTime,
@ -113,6 +115,7 @@ export function MotionReviewTimeline({
draggableElementTime: handlebarTime, draggableElementTime: handlebarTime,
setDraggableElementTime: setHandlebarTime, setDraggableElementTime: setHandlebarTime,
timelineDuration, timelineDuration,
timelineCollapsed: motionOnly,
timelineStartAligned, timelineStartAligned,
isDragging, isDragging,
setIsDragging, setIsDragging,
@ -176,6 +179,7 @@ export function MotionReviewTimeline({
segmentDuration={segmentDuration} segmentDuration={segmentDuration}
segmentTime={segmentTime} segmentTime={segmentTime}
timestampSpread={timestampSpread} timestampSpread={timestampSpread}
motionOnly={motionOnly}
showMinimap={showMinimap} showMinimap={showMinimap}
minimapStartTime={minimapStartTime} minimapStartTime={minimapStartTime}
minimapEndTime={minimapEndTime} minimapEndTime={minimapEndTime}
@ -195,6 +199,7 @@ export function MotionReviewTimeline({
minimapEndTime, minimapEndTime,
events, events,
motion_events, motion_events,
motionOnly,
]); ]);
const segments = useMemo( const segments = useMemo(
@ -211,6 +216,7 @@ export function MotionReviewTimeline({
minimapEndTime, minimapEndTime,
events, events,
motion_events, motion_events,
motionOnly,
], ],
); );

View File

@ -14,6 +14,7 @@ type MotionSegmentProps = {
segmentTime: number; segmentTime: number;
segmentDuration: number; segmentDuration: number;
timestampSpread: number; timestampSpread: number;
motionOnly: boolean;
showMinimap: boolean; showMinimap: boolean;
minimapStartTime?: number; minimapStartTime?: number;
minimapEndTime?: number; minimapEndTime?: number;
@ -26,6 +27,7 @@ export function MotionSegment({
segmentTime, segmentTime,
segmentDuration, segmentDuration,
timestampSpread, timestampSpread,
motionOnly,
showMinimap, showMinimap,
minimapStartTime, minimapStartTime,
minimapEndTime, minimapEndTime,
@ -180,6 +182,11 @@ export function MotionSegment({
}, [segmentTime, setHandlebarTime]); }, [segmentTime, setHandlebarTime]);
return ( return (
<>
{(((firstHalfSegmentWidth > 1 || secondHalfSegmentWidth > 1) &&
motionOnly &&
severity[0] < 2) ||
!motionOnly) && (
<div <div
key={segmentKey} key={segmentKey}
data-segment-id={segmentKey} data-segment-id={segmentKey}
@ -187,6 +194,8 @@ export function MotionSegment({
onClick={segmentClick} onClick={segmentClick}
onTouchEnd={(event) => handleTouchStart(event, segmentClick)} onTouchEnd={(event) => handleTouchStart(event, segmentClick)}
> >
{!motionOnly && (
<>
<MinimapBounds <MinimapBounds
isFirstSegmentInMinimap={isFirstSegmentInMinimap} isFirstSegmentInMinimap={isFirstSegmentInMinimap}
isLastSegmentInMinimap={isLastSegmentInMinimap} isLastSegmentInMinimap={isLastSegmentInMinimap}
@ -195,15 +204,22 @@ export function MotionSegment({
firstMinimapSegmentRef={firstMinimapSegmentRef} firstMinimapSegmentRef={firstMinimapSegmentRef}
/> />
<Tick timestamp={timestamp} timestampSpread={timestampSpread} /> <Tick
key={`${segmentKey}_tick`}
timestamp={timestamp}
timestampSpread={timestampSpread}
/>
<Timestamp <Timestamp
key={`${segmentKey}_timestamp`}
isFirstSegmentInMinimap={isFirstSegmentInMinimap} isFirstSegmentInMinimap={isFirstSegmentInMinimap}
isLastSegmentInMinimap={isLastSegmentInMinimap} isLastSegmentInMinimap={isLastSegmentInMinimap}
timestamp={timestamp} timestamp={timestamp}
timestampSpread={timestampSpread} timestampSpread={timestampSpread}
segmentKey={segmentKey} segmentKey={segmentKey}
/> />
</>
)}
<div className="absolute left-1/2 transform -translate-x-1/2 w-[20px] md:w-[40px] h-2 z-10 cursor-pointer"> <div className="absolute left-1/2 transform -translate-x-1/2 w-[20px] md:w-[40px] h-2 z-10 cursor-pointer">
<div className="flex flex-row justify-center w-[20px] md:w-[40px] mb-[1px]"> <div className="flex flex-row justify-center w-[20px] md:w-[40px] mb-[1px]">
@ -231,7 +247,8 @@ export function MotionSegment({
</div> </div>
</div> </div>
{severity.map((severityValue: number, index: number) => { {!motionOnly &&
severity.map((severityValue: number, index: number) => {
if (severityValue > 1) { if (severityValue > 1) {
return ( return (
<React.Fragment key={index}> <React.Fragment key={index}>
@ -253,6 +270,8 @@ export function MotionSegment({
} }
})} })}
</div> </div>
)}
</>
); );
} }

View File

@ -1,7 +1,7 @@
import * as React from "react" import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch" import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Switch = React.forwardRef< const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>, React.ElementRef<typeof SwitchPrimitives.Root>,
@ -10,18 +10,18 @@ const Switch = React.forwardRef<
<SwitchPrimitives.Root <SwitchPrimitives.Root
className={cn( className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-selected data-[state=unchecked]:bg-input", "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-selected data-[state=unchecked]:bg-input",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
> >
<SwitchPrimitives.Thumb <SwitchPrimitives.Thumb
className={cn( className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" "pointer-events-none block h-5 w-5 rounded-full bg-muted-foreground shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
)} )}
/> />
</SwitchPrimitives.Root> </SwitchPrimitives.Root>
)) ));
Switch.displayName = SwitchPrimitives.Root.displayName Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch } export { Switch };

View File

@ -15,6 +15,7 @@ type DraggableElementProps = {
setDraggableElementTime?: React.Dispatch<React.SetStateAction<number>>; setDraggableElementTime?: React.Dispatch<React.SetStateAction<number>>;
draggableElementTimeRef: React.MutableRefObject<HTMLDivElement | null>; draggableElementTimeRef: React.MutableRefObject<HTMLDivElement | null>;
timelineDuration: number; timelineDuration: number;
timelineCollapsed?: boolean;
timelineStartAligned: number; timelineStartAligned: number;
isDragging: boolean; isDragging: boolean;
setIsDragging: React.Dispatch<React.SetStateAction<boolean>>; setIsDragging: React.Dispatch<React.SetStateAction<boolean>>;
@ -33,6 +34,7 @@ function useDraggableElement({
setDraggableElementTime, setDraggableElementTime,
draggableElementTimeRef, draggableElementTimeRef,
timelineDuration, timelineDuration,
timelineCollapsed,
timelineStartAligned, timelineStartAligned,
isDragging, isDragging,
setIsDragging, setIsDragging,
@ -40,6 +42,7 @@ function useDraggableElement({
}: DraggableElementProps) { }: DraggableElementProps) {
const [clientYPosition, setClientYPosition] = useState<number | null>(null); const [clientYPosition, setClientYPosition] = useState<number | null>(null);
const [initialClickAdjustment, setInitialClickAdjustment] = useState(0); const [initialClickAdjustment, setInitialClickAdjustment] = useState(0);
const [segments, setSegments] = useState<HTMLDivElement[]>([]);
const { alignStartDateToTimeline, getCumulativeScrollTop } = useTimelineUtils( const { alignStartDateToTimeline, getCumulativeScrollTop } = useTimelineUtils(
{ {
segmentDuration: segmentDuration, segmentDuration: segmentDuration,
@ -101,7 +104,7 @@ function useDraggableElement({
} else if (e.nativeEvent instanceof MouseEvent) { } else if (e.nativeEvent instanceof MouseEvent) {
clientY = e.nativeEvent.clientY; clientY = e.nativeEvent.clientY;
} }
if (clientY && draggableElementRef.current && isDesktop) { if (clientY && draggableElementRef.current) {
const draggableElementRect = const draggableElementRect =
draggableElementRef.current.getBoundingClientRect(); draggableElementRef.current.getBoundingClientRect();
if (!isDragging) { if (!isDragging) {
@ -203,6 +206,12 @@ function useDraggableElement({
[contentRef, draggableElementRef, timelineRef, getClientYPosition], [contentRef, draggableElementRef, timelineRef, getClientYPosition],
); );
useEffect(() => {
if (timelineRef.current) {
setSegments(Array.from(timelineRef.current.querySelectorAll(".segment")));
}
}, [timelineRef, segmentDuration, timelineDuration, timelineCollapsed]);
useEffect(() => { useEffect(() => {
let animationFrameId: number | null = null; let animationFrameId: number | null = null;
@ -211,13 +220,11 @@ function useDraggableElement({
timelineRef.current && timelineRef.current &&
showDraggableElement && showDraggableElement &&
isDragging && isDragging &&
clientYPosition clientYPosition &&
segments
) { ) {
const { const { scrollHeight: timelineHeight, scrollTop: scrolled } =
scrollHeight: timelineHeight, timelineRef.current;
scrollTop: scrolled,
offsetTop: timelineTop,
} = timelineRef.current;
const segmentHeight = const segmentHeight =
timelineHeight / (timelineDuration / segmentDuration); timelineHeight / (timelineDuration / segmentDuration);
@ -235,22 +242,53 @@ function useDraggableElement({
? timestampToPixels(draggableElementLatestTime) ? timestampToPixels(draggableElementLatestTime)
: segmentHeight * 2 + scrolled; : segmentHeight * 2 + scrolled;
const timelineRect = timelineRef.current.getBoundingClientRect();
const timelineTopAbsolute = timelineRect.top;
const newElementPosition = Math.min( const newElementPosition = Math.min(
elementEarliest, elementEarliest,
Math.max( Math.max(
elementLatest, elementLatest,
// current Y position // current Y position
clientYPosition - clientYPosition -
timelineTop + timelineTopAbsolute +
parentScrollTop - parentScrollTop -
initialClickAdjustment, initialClickAdjustment,
), ),
); );
const segmentIndex = Math.floor(newElementPosition / segmentHeight); if (
const segmentStartTime = alignStartDateToTimeline( newElementPosition >= elementEarliest ||
timelineStartAligned - segmentIndex * segmentDuration, newElementPosition <= elementLatest
) {
return;
}
let targetSegmentId = 0;
let offset = 0;
segments.forEach((segmentElement: HTMLDivElement) => {
const rect = segmentElement.getBoundingClientRect();
const segmentTop =
rect.top + scrolled - timelineTopAbsolute - segmentHeight;
const segmentBottom =
rect.bottom + scrolled - timelineTopAbsolute - segmentHeight;
// Check if handlebar position falls within the segment bounds
if (
newElementPosition >= segmentTop &&
newElementPosition <= segmentBottom
) {
targetSegmentId = parseFloat(
segmentElement.getAttribute("data-segment-id") || "0",
); );
offset = Math.min(
segmentBottom - newElementPosition,
segmentHeight,
);
return;
}
});
if (draggingAtTopEdge || draggingAtBottomEdge) { if (draggingAtTopEdge || draggingAtBottomEdge) {
let newPosition = clientYPosition; let newPosition = clientYPosition;
@ -267,17 +305,15 @@ function useDraggableElement({
} }
updateDraggableElementPosition( updateDraggableElementPosition(
newElementPosition - segmentHeight, newElementPosition,
segmentStartTime, targetSegmentId,
false, false,
false, false,
); );
if (setDraggableElementTime) { if (setDraggableElementTime) {
setDraggableElementTime( setDraggableElementTime(
timelineStartAligned - targetSegmentId + segmentDuration * (offset / segmentHeight),
((newElementPosition - segmentHeight / 2 - 2) / segmentHeight) *
segmentDuration,
); );
} }
@ -321,7 +357,8 @@ function useDraggableElement({
draggableElementRef.current && draggableElementRef.current &&
showDraggableElement && showDraggableElement &&
draggableElementTime && draggableElementTime &&
!isDragging !isDragging &&
segments.length > 0
) { ) {
const { scrollHeight: timelineHeight, scrollTop: scrolled } = const { scrollHeight: timelineHeight, scrollTop: scrolled } =
timelineRef.current; timelineRef.current;
@ -329,14 +366,39 @@ function useDraggableElement({
const segmentHeight = const segmentHeight =
timelineHeight / (timelineDuration / segmentDuration); timelineHeight / (timelineDuration / segmentDuration);
const parentScrollTop = getCumulativeScrollTop(timelineRef.current); const alignedSegmentTime = alignStartDateToTimeline(draggableElementTime);
const newElementPosition = let segmentElement = timelineRef.current.querySelector(
((timelineStartAligned - draggableElementTime) / segmentDuration) * `[data-segment-id="${alignedSegmentTime}"]`,
segmentHeight + );
parentScrollTop -
scrolled - if (!segmentElement) {
2; // height of draggableElement horizontal line // segment not found, maybe we collapsed over a collapsible segment
let searchTime = alignedSegmentTime;
while (searchTime >= timelineStartAligned - timelineDuration) {
// Decrement currentTime by segmentDuration
searchTime -= segmentDuration;
segmentElement = timelineRef.current.querySelector(
`[data-segment-id="${searchTime}"]`,
);
if (segmentElement) {
// segmentElement found
break;
}
}
}
if (segmentElement) {
const timelineRect = timelineRef.current.getBoundingClientRect();
const timelineTopAbsolute = timelineRect.top;
const rect = segmentElement.getBoundingClientRect();
const segmentTop =
rect.top + scrolled - timelineTopAbsolute - segmentHeight / 2;
const offset =
((draggableElementTime - alignedSegmentTime) / segmentDuration) *
segmentHeight;
const newElementPosition = segmentTop - offset;
updateDraggableElementPosition( updateDraggableElementPosition(
newElementPosition, newElementPosition,
@ -345,13 +407,19 @@ function useDraggableElement({
true, true,
); );
} }
}
// we know that these deps are correct // we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
draggableElementTime, draggableElementTime,
timelineDuration,
segmentDuration,
showDraggableElement, showDraggableElement,
draggableElementRef, draggableElementRef,
timelineStartAligned, timelineStartAligned,
timelineRef,
timelineCollapsed,
segments,
]); ]);
return { handleMouseDown, handleMouseUp, handleMouseMove }; return { handleMouseDown, handleMouseUp, handleMouseMove };

View File

@ -196,6 +196,8 @@ export default function EventView({
[reviewItems], [reviewItems],
); );
const [motionOnly, setMotionOnly] = useState(false);
if (!config) { if (!config) {
return <ActivityIndicator />; return <ActivityIndicator />;
} }
@ -253,6 +255,9 @@ export default function EventView({
reviewSummary={reviewSummary} reviewSummary={reviewSummary}
filter={filter} filter={filter}
onUpdateFilter={updateFilter} onUpdateFilter={updateFilter}
severity={severity}
motionOnly={motionOnly}
setMotionOnly={setMotionOnly}
/> />
) : ( ) : (
<ReviewActionGroup <ReviewActionGroup
@ -290,6 +295,7 @@ export default function EventView({
timeRange={timeRange} timeRange={timeRange}
startTime={startTime} startTime={startTime}
filter={filter} filter={filter}
motionOnly={motionOnly}
onOpenRecording={onOpenRecording} onOpenRecording={onOpenRecording}
/> />
)} )}
@ -603,6 +609,7 @@ type MotionReviewProps = {
timeRange: { before: number; after: number }; timeRange: { before: number; after: number };
startTime?: number; startTime?: number;
filter?: ReviewFilter; filter?: ReviewFilter;
motionOnly?: boolean;
onOpenRecording: (data: RecordingStartingPoint) => void; onOpenRecording: (data: RecordingStartingPoint) => void;
}; };
function MotionReview({ function MotionReview({
@ -612,6 +619,7 @@ function MotionReview({
timeRange, timeRange,
startTime, startTime,
filter, filter,
motionOnly = false,
onOpenRecording, onOpenRecording,
}: MotionReviewProps) { }: MotionReviewProps) {
const segmentDuration = 30; const segmentDuration = 30;
@ -784,6 +792,7 @@ function MotionReview({
timestampSpread={15} timestampSpread={15}
timelineStart={timeRangeSegments.end} timelineStart={timeRangeSegments.end}
timelineEnd={timeRangeSegments.start} timelineEnd={timeRangeSegments.start}
motionOnly={motionOnly}
showHandlebar showHandlebar
handlebarTime={currentTime} handlebarTime={currentTime}
setHandlebarTime={setCurrentTime} setHandlebarTime={setCurrentTime}

View File

@ -55,7 +55,7 @@
--border: 214.3 31.8% 91.4%; --border: 214.3 31.8% 91.4%;
--input: hsl(214.3 31.8% 91.4%); --input: hsl(214.3 31.8% 91.4%);
--input: 214.3 31.8% 91.4%; --input: 0 0 85%;
--ring: hsl(222.2 84% 4.9%); --ring: hsl(222.2 84% 4.9%);
--ring: 222.2 84% 4.9%; --ring: 222.2 84% 4.9%;
@ -140,7 +140,7 @@
--border: 0 0% 32%; --border: 0 0% 32%;
--input: hsl(217.2 32.6% 17.5%); --input: hsl(217.2 32.6% 17.5%);
--input: 217.2 32.6% 17.5%; --input: 0 0 25%;
--ring: hsl(212.7 26.8% 83.9%); --ring: hsl(212.7 26.8% 83.9%);
--ring: 212.7 26.8% 83.9%; --ring: 212.7 26.8% 83.9%;