diff --git a/studio/frontend/src/features/chat/components/chat-search-dialog.tsx b/studio/frontend/src/features/chat/components/chat-search-dialog.tsx index 85480359b4..676dcc23d5 100644 --- a/studio/frontend/src/features/chat/components/chat-search-dialog.tsx +++ b/studio/frontend/src/features/chat/components/chat-search-dialog.tsx @@ -16,6 +16,21 @@ import { useEffect } from "react"; import { useChatSearchIndex } from "../hooks/use-chat-search-index"; import { useChatSearchStore } from "../stores/chat-search-store"; +// cmdk's default fuzzy scorer keeps non-matching rows visible (issue #5572), so +// require every whitespace token to be a substring of the item's keywords. +// `value` is the unique thread id (cmdk selection); title/preview come via keywords. +export function chatSearchFilter( + _value: string, + search: string, + keywords?: string[], +): number { + const query = search.trim().toLowerCase(); + if (query === "") return 1; + const haystack = (keywords ?? []).join(" ").toLowerCase(); + const tokens = query.split(/\s+/); + return tokens.every((token) => haystack.includes(token)) ? 1 : 0; +} + function formatRelative(createdAt: number): string { const diff = Date.now() - createdAt; const day = 86_400_000; @@ -52,7 +67,7 @@ export function ChatSearchDialog() { className="corner-squircle top-[25%] w-[635px] max-w-[calc(100%-2rem)] gap-0 p-0 sm:max-w-[635px] border-0 ring-0 shadow-[0_10px_34px_rgba(0,0,0,0.12)] dark:border dark:border-border dark:ring-0 dark:shadow-none" overlayClassName="bg-transparent" > - +
( { navigate({ to: "/chat",