* Object lifecycle and semantic search UI tweaks

* prevent console errors for sheet component
This commit is contained in:
Josh Hawkins
2024-09-09 09:33:38 -05:00
committed by GitHub
parent 8be139d4d1
commit f143fceceb
9 changed files with 174 additions and 47 deletions

View File

@@ -1,17 +1,26 @@
import SearchFilterGroup from "@/components/filter/SearchFilterGroup";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import Chip from "@/components/indicators/Chip";
import SearchDetailDialog from "@/components/overlay/detail/SearchDetailDialog";
import SearchThumbnailPlayer from "@/components/player/SearchThumbnailPlayer";
import { Input } from "@/components/ui/input";
import { Toaster } from "@/components/ui/sonner";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { Preview } from "@/types/preview";
import { SearchFilter, SearchResult } from "@/types/search";
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { isMobileOnly } from "react-device-detect";
import {
LuExternalLink,
LuImage,
LuSearchCheck,
LuSearchX,
LuText,
LuXCircle,
} from "react-icons/lu";
import { Link } from "react-router-dom";
@@ -40,6 +49,15 @@ export default function SearchView({
onUpdateFilter,
onOpenSearch,
}: SearchViewProps) {
// remove duplicate event ids
const uniqueResults = useMemo(() => {
return searchResults?.filter(
(value, index, self) =>
index === self.findIndex((v) => v.id === value.id),
);
}, [searchResults]);
// detail
const [searchDetail, setSearchDetail] = useState<SearchResult>();
@@ -57,6 +75,25 @@ export default function SearchView({
[onOpenSearch],
);
// confidence score - probably needs tweaking
const zScoreToConfidence = (score: number, source: string) => {
let midpoint, scale;
if (source === "thumbnail") {
midpoint = 2;
scale = 0.5;
} else {
midpoint = 0.5;
scale = 1.5;
}
// Sigmoid function: 1 / (1 + e^x)
const confidence = 1 / (1 + Math.exp((score - midpoint) * scale));
return Math.round(confidence * 100);
};
return (
<div className="flex size-full flex-col pt-2 md:py-2">
<Toaster closeButton={true} />
@@ -69,10 +106,12 @@ export default function SearchView({
/>
<div className="relative mb-2 flex h-11 items-center justify-between pl-2 pr-2 md:pl-3">
<div className="relative w-full md:w-1/3">
<div className="relative mr-3 w-full md:w-1/3">
<Input
className="text-md w-full bg-muted pr-10"
placeholder="Search for a specific detected object..."
placeholder={
isMobileOnly ? "Search" : "Search for a detected object..."
}
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
@@ -124,8 +163,8 @@ export default function SearchView({
)}
<div className="grid w-full gap-2 px-1 sm:grid-cols-2 md:mx-2 md:grid-cols-4 md:gap-4 3xl:grid-cols-6">
{searchResults &&
searchResults.map((value) => {
{uniqueResults &&
uniqueResults.map((value) => {
const selected = false;
return (
@@ -145,6 +184,34 @@ export default function SearchView({
scrollLock={false}
onClick={onSelectSearch}
/>
<div className={cn("absolute right-2 top-2 z-40")}>
<Tooltip>
<TooltipTrigger>
<Chip
className={`flex select-none items-center justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize text-white`}
>
{value.search_source == "thumbnail" ? (
<LuImage className="mr-1 size-3" />
) : (
<LuText className="mr-1 size-3" />
)}
{zScoreToConfidence(
value.search_distance,
value.search_source,
)}
%
</Chip>
</TooltipTrigger>
<TooltipContent>
Matched {value.search_source} at{" "}
{zScoreToConfidence(
value.search_distance,
value.search_source,
)}
%
</TooltipContent>
</Tooltip>
</div>
</div>
<div
className={`review-item-ring pointer-events-none absolute inset-0 z-10 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${selected ? `shadow-severity_alert outline-severity_alert` : "outline-transparent duration-500"}`}