diff --git a/web/src/components/chat/ChatEventThumbnailsRow.tsx b/web/src/components/chat/ChatEventThumbnailsRow.tsx
new file mode 100644
index 000000000..bf2c5e88f
--- /dev/null
+++ b/web/src/components/chat/ChatEventThumbnailsRow.tsx
@@ -0,0 +1,42 @@
+import { useApiHost } from "@/api";
+
+type ChatEventThumbnailsRowProps = {
+ events: { id: string }[];
+};
+
+/**
+ * Horizontal scroll row of event thumbnail images for chat (e.g. after search_objects).
+ * Renders nothing when events is empty.
+ */
+export function ChatEventThumbnailsRow({
+ events,
+}: ChatEventThumbnailsRowProps) {
+ const apiHost = useApiHost();
+
+ if (events.length === 0) return null;
+
+ return (
+
+
+
+ {events.map((event) => (
+
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/web/src/pages/Chat.tsx b/web/src/pages/Chat.tsx
index 01912b77e..ac812b58e 100644
--- a/web/src/pages/Chat.tsx
+++ b/web/src/pages/Chat.tsx
@@ -4,10 +4,14 @@ import { FaArrowUpLong } from "react-icons/fa6";
import { useTranslation } from "react-i18next";
import { useState, useCallback } from "react";
import axios from "axios";
+import { ChatEventThumbnailsRow } from "@/components/chat/ChatEventThumbnailsRow";
import { MessageBubble } from "@/components/chat/ChatMessage";
import { ToolCallBubble } from "@/components/chat/ToolCallBubble";
import type { ChatMessage } from "@/types/chat";
-import { streamChatCompletion } from "@/utils/chatUtil";
+import {
+ getEventIdsFromSearchObjectsToolCalls,
+ streamChatCompletion,
+} from "@/utils/chatUtil";
export default function ChatPage() {
const { t } = useTranslation(["views/chat"]);
@@ -118,6 +122,15 @@ export default function ChatPage() {
msg.role === "user" || !isLoading || i < messages.length - 1
}
/>
+ {msg.role === "assistant" &&
+ (() => {
+ const isComplete = !isLoading || i < messages.length - 1;
+ if (!isComplete) return null;
+ const events = getEventIdsFromSearchObjectsToolCalls(
+ msg.toolCalls,
+ );
+ return ;
+ })()}
);
})}
diff --git a/web/src/utils/chatUtil.ts b/web/src/utils/chatUtil.ts
index 5eb26bb72..323475a21 100644
--- a/web/src/utils/chatUtil.ts
+++ b/web/src/utils/chatUtil.ts
@@ -161,3 +161,33 @@ export async function streamChatCompletion(
onDone();
}
}
+
+/**
+ * Parse search_objects tool call response(s) into event ids for thumbnails.
+ */
+export function getEventIdsFromSearchObjectsToolCalls(
+ toolCalls: ToolCall[] | undefined,
+): { id: string }[] {
+ if (!toolCalls?.length) return [];
+ const results: { id: string }[] = [];
+ for (const tc of toolCalls) {
+ if (tc.name !== "search_objects" || !tc.response?.trim()) continue;
+ try {
+ const parsed = JSON.parse(tc.response) as unknown;
+ if (!Array.isArray(parsed)) continue;
+ for (const item of parsed) {
+ if (
+ item &&
+ typeof item === "object" &&
+ "id" in item &&
+ typeof (item as { id: unknown }).id === "string"
+ ) {
+ results.push({ id: (item as { id: string }).id });
+ }
+ }
+ } catch {
+ // ignore parse errors
+ }
+ }
+ return results;
+}