From 78e278d5e86ae3761d41ca83fb4dbeabb3a46a9b Mon Sep 17 00:00:00 2001
From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
Date: Wed, 5 Nov 2025 20:49:28 -0600
Subject: [PATCH] turn annotation settings into a popover
---
.../overlay/detail/AnnotationSettingsPane.tsx | 2 +-
.../overlay/detail/SearchDetailDialog.tsx | 69 +++++++++++++++----
.../overlay/detail/TrackingDetails.tsx | 25 +------
3 files changed, 59 insertions(+), 37 deletions(-)
diff --git a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx
index c180502f4..94c44ecdf 100644
--- a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx
+++ b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx
@@ -140,7 +140,7 @@ export function AnnotationSettingsPane({
}
return (
-
+
{t("trackingDetails.annotationSettings.title")}
diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx
index c6581a796..d3d0b8e5c 100644
--- a/web/src/components/overlay/detail/SearchDetailDialog.tsx
+++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx
@@ -32,6 +32,7 @@ import {
FaChevronRight,
} from "react-icons/fa";
import { TrackingDetails } from "./TrackingDetails";
+import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
import { LuSettings } from "react-icons/lu";
import { DetailStreamProvider } from "@/context/detail-stream-context";
import {
@@ -74,6 +75,7 @@ import { useIsAdmin } from "@/hooks/use-is-admin";
import { getTranslatedLabel } from "@/utils/i18n";
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
import { DialogPortal } from "@radix-ui/react-dialog";
+import { useDetailStream } from "@/context/detail-stream-context";
const SEARCH_TABS = ["snapshot", "tracking_details"] as const;
export type SearchTab = (typeof SEARCH_TABS)[number];
@@ -147,17 +149,46 @@ function TabsWithActions({
setSearch={setSearch}
setSimilarity={setSimilarity}
/>
-
+ {pageToggle === "tracking_details" && (
+
+ )}
+
+ );
+}
+
+type AnnotationSettingsPopoverProps = {
+ search: SearchResult;
+ showControls?: boolean;
+ setShowControls?: (v: boolean) => void;
+};
+
+function AnnotationSettingsPopover({
+ search,
+ showControls,
+ setShowControls,
+}: AnnotationSettingsPopoverProps) {
+ const { t } = useTranslation(["views/explore"]);
+ const { annotationOffset, setAnnotationOffset } = useDetailStream();
+ const [showZones, setShowZones] = useState(true);
+
+ return (
+
+
-
+
+
+
@@ -165,7 +196,23 @@ function TabsWithActions({
-
+
+ {
+ if (typeof value === "function") {
+ const newValue = value(annotationOffset);
+ setAnnotationOffset(newValue);
+ } else {
+ setAnnotationOffset(value);
+ }
+ }}
+ />
+
+
);
}
@@ -221,8 +268,6 @@ function DialogContentComponent({
/>
) : undefined
}
- showControls={showControls}
- setShowControls={setShowControls}
/>
);
}
diff --git a/web/src/components/overlay/detail/TrackingDetails.tsx b/web/src/components/overlay/detail/TrackingDetails.tsx
index 6afb90cce..df4c92c30 100644
--- a/web/src/components/overlay/detail/TrackingDetails.tsx
+++ b/web/src/components/overlay/detail/TrackingDetails.tsx
@@ -8,8 +8,6 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { getIconForLabel } from "@/utils/iconUtil";
import { LuCircle, LuFolderX } from "react-icons/lu";
import { cn } from "@/lib/utils";
-import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
-
import HlsVideoPlayer from "@/components/player/HlsVideoPlayer";
import { baseUrl } from "@/api/baseUrl";
import { REVIEW_PADDING } from "@/types/review";
@@ -40,15 +38,12 @@ type TrackingDetailsProps = {
event: Event;
fullscreen?: boolean;
tabs?: React.ReactNode;
- showControls?: boolean;
- setShowControls?: (v: boolean) => void;
};
export function TrackingDetails({
className,
event,
tabs,
- showControls = false,
}: TrackingDetailsProps) {
const videoRef = useRef
(null);
const { t } = useTranslation(["views/explore"]);
@@ -58,8 +53,7 @@ export function TrackingDetails({
const [displaySource, _setDisplaySource] = useState<"video" | "image">(
"video",
);
- const { setSelectedObjectIds, annotationOffset, setAnnotationOffset } =
- useDetailStream();
+ const { setSelectedObjectIds, annotationOffset } = useDetailStream();
// manualOverride holds a record-stream timestamp explicitly chosen by the
// user (eg, clicking a lifecycle row). When null we display `currentTime`.
@@ -90,7 +84,6 @@ export function TrackingDetails({
const containerRef = useRef(null);
const [_selectedZone, setSelectedZone] = useState("");
const [_lifecycleZones, setLifecycleZones] = useState([]);
- const [showZones, setShowZones] = useState(true);
const [seekToTimestamp, setSeekToTimestamp] = useState(null);
const aspectRatio = useMemo(() => {
@@ -464,22 +457,6 @@ export function TrackingDetails({
{t("trackingDetails.autoTrackingTips")}
)}
- {showControls && (
-