2026-02-27 19:07:30 +03:00
|
|
|
import { useApiHost } from "@/api";
|
2026-05-17 23:40:33 +03:00
|
|
|
import { baseUrl } from "@/api/baseUrl";
|
2026-04-09 23:31:37 +03:00
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import { LuExternalLink } from "react-icons/lu";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
|
|
|
|
|
type ChatEvent = { id: string; score?: number };
|
2026-02-27 19:07:30 +03:00
|
|
|
|
|
|
|
|
type ChatEventThumbnailsRowProps = {
|
2026-04-09 23:31:37 +03:00
|
|
|
events: ChatEvent[];
|
|
|
|
|
anchor?: { id: string } | null;
|
|
|
|
|
onAttach?: (eventId: string) => void;
|
2026-02-27 19:07:30 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-09 23:31:37 +03:00
|
|
|
* Horizontal scroll row of event thumbnail images for chat.
|
|
|
|
|
* Optionally renders an anchor thumbnail with a "reference" badge above the
|
|
|
|
|
* results, and per-event similarity scores when provided.
|
|
|
|
|
* Clicking a thumbnail calls onAttach; a small external-link overlay opens
|
|
|
|
|
* the event in Explore.
|
|
|
|
|
* Renders nothing when there is nothing to show.
|
2026-02-27 19:07:30 +03:00
|
|
|
*/
|
|
|
|
|
export function ChatEventThumbnailsRow({
|
|
|
|
|
events,
|
2026-04-09 23:31:37 +03:00
|
|
|
anchor = null,
|
|
|
|
|
onAttach,
|
2026-02-27 19:07:30 +03:00
|
|
|
}: ChatEventThumbnailsRowProps) {
|
|
|
|
|
const apiHost = useApiHost();
|
2026-04-09 23:31:37 +03:00
|
|
|
const { t } = useTranslation(["views/chat"]);
|
2026-02-27 19:07:30 +03:00
|
|
|
|
2026-04-09 23:31:37 +03:00
|
|
|
if (events.length === 0 && !anchor) return null;
|
|
|
|
|
|
|
|
|
|
const renderThumb = (event: ChatEvent, isAnchor = false) => (
|
|
|
|
|
<div
|
|
|
|
|
key={event.id}
|
2026-05-14 20:05:38 +03:00
|
|
|
className="relative aspect-square size-32 shrink-0 overflow-hidden rounded-lg"
|
2026-04-09 23:31:37 +03:00
|
|
|
>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="block size-full"
|
|
|
|
|
onClick={() => onAttach?.(event.id)}
|
|
|
|
|
aria-label={t("attach_event_aria", { eventId: event.id })}
|
|
|
|
|
>
|
|
|
|
|
<img
|
|
|
|
|
className="size-full object-cover"
|
|
|
|
|
src={`${apiHost}api/events/${event.id}/thumbnail.webp`}
|
|
|
|
|
alt=""
|
|
|
|
|
loading="lazy"
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<a
|
2026-05-17 23:40:33 +03:00
|
|
|
href={`${baseUrl}explore?event_id=${event.id}`}
|
2026-04-09 23:31:37 +03:00
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
className="absolute right-1 top-1 flex size-6 items-center justify-center rounded bg-black/60 text-white hover:bg-black/80"
|
|
|
|
|
aria-label={t("open_in_explore")}
|
|
|
|
|
>
|
|
|
|
|
<LuExternalLink className="size-3" />
|
|
|
|
|
</a>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>{t("open_in_explore")}</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
{isAnchor && (
|
2026-05-14 20:05:38 +03:00
|
|
|
<>
|
|
|
|
|
<span
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
className="pointer-events-none absolute inset-0 rounded-lg ring-2 ring-inset ring-primary"
|
|
|
|
|
/>
|
|
|
|
|
<span className="pointer-events-none absolute left-1 top-1 rounded bg-primary px-1 text-[10px] text-primary-foreground">
|
|
|
|
|
{t("anchor")}
|
|
|
|
|
</span>
|
|
|
|
|
</>
|
2026-04-09 23:31:37 +03:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-02-27 19:07:30 +03:00
|
|
|
|
|
|
|
|
return (
|
2026-04-09 23:31:37 +03:00
|
|
|
<div className="flex min-w-0 max-w-full flex-col gap-2 self-start">
|
|
|
|
|
{anchor && (
|
|
|
|
|
<div className="scrollbar-container min-w-0 overflow-x-auto">
|
|
|
|
|
<div className="flex w-max gap-2">{renderThumb(anchor, true)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{events.length > 0 && (
|
|
|
|
|
<div className="scrollbar-container min-w-0 overflow-x-auto">
|
|
|
|
|
<div className="flex w-max gap-2">
|
|
|
|
|
{events.map((event) => renderThumb(event))}
|
|
|
|
|
</div>
|
2026-02-27 19:07:30 +03:00
|
|
|
</div>
|
2026-04-09 23:31:37 +03:00
|
|
|
)}
|
2026-02-27 19:07:30 +03:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|