Chat search (#1224)
Based on https://github.com/dyad-sh/dyad/pull/1116 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds a fast chat search dialog (Command Palette) to find and jump between chats. Open via the sidebar button or Ctrl/Cmd+K, with title and message text search plus inline snippets. - New Features - Command palette using cmdk with keyboard shortcut (Ctrl/Cmd+K). - Searches within the selected app across chat titles and message content via a new IPC route (search-chats). - Debounced queries (150ms) with React Query; results de-duplicated and sorted by newest. - Snippet preview with highlighted matches and custom ranking; selecting a result navigates and closes the dialog. - Search button added to ChatList; basic e2e tests added (currently skipped). - Dependencies - Added cmdk@1.1.1. - Bumped @radix-ui/react-dialog to ^1.1.15 and updated Dialog to support an optional close button. <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Evans Obeng <iamevansobeng@outlook.com> Co-authored-by: Evans Obeng <60653146+iamevansobeng@users.noreply.github.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { PlusCircle, MoreVertical, Trash2, Edit3 } from "lucide-react";
|
||||
import { PlusCircle, MoreVertical, Trash2, Edit3, Search } from "lucide-react";
|
||||
import { useAtom } from "jotai";
|
||||
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
||||
@@ -27,11 +27,14 @@ import { useChats } from "@/hooks/useChats";
|
||||
import { RenameChatDialog } from "@/components/chat/RenameChatDialog";
|
||||
import { DeleteChatDialog } from "@/components/chat/DeleteChatDialog";
|
||||
|
||||
import { ChatSearchDialog } from "./ChatSearchDialog";
|
||||
|
||||
export function ChatList({ show }: { show?: boolean }) {
|
||||
const navigate = useNavigate();
|
||||
const [selectedChatId, setSelectedChatId] = useAtom(selectedChatIdAtom);
|
||||
const [selectedAppId, setSelectedAppId] = useAtom(selectedAppIdAtom);
|
||||
const [, setIsDropdownOpen] = useAtom(dropdownOpenAtom);
|
||||
|
||||
const { chats, loading, refreshChats } = useChats(selectedAppId);
|
||||
const routerState = useRouterState();
|
||||
const isChatRoute = routerState.location.pathname === "/chat";
|
||||
@@ -46,6 +49,9 @@ export function ChatList({ show }: { show?: boolean }) {
|
||||
const [deleteChatId, setDeleteChatId] = useState<number | null>(null);
|
||||
const [deleteChatTitle, setDeleteChatTitle] = useState("");
|
||||
|
||||
// search dialog state
|
||||
const [isSearchDialogOpen, setIsSearchDialogOpen] = useState(false);
|
||||
|
||||
// Update selectedChatId when route changes
|
||||
useEffect(() => {
|
||||
if (isChatRoute) {
|
||||
@@ -70,6 +76,7 @@ export function ChatList({ show }: { show?: boolean }) {
|
||||
}) => {
|
||||
setSelectedChatId(chatId);
|
||||
setSelectedAppId(appId);
|
||||
setIsSearchDialogOpen(false);
|
||||
navigate({
|
||||
to: "/chat",
|
||||
search: { id: chatId },
|
||||
@@ -151,7 +158,10 @@ export function ChatList({ show }: { show?: boolean }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarGroup className="overflow-y-auto h-[calc(100vh-112px)]">
|
||||
<SidebarGroup
|
||||
className="overflow-y-auto h-[calc(100vh-112px)]"
|
||||
data-testid="chat-list-container"
|
||||
>
|
||||
<SidebarGroupLabel>Recent Chats</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<div className="flex flex-col space-y-4">
|
||||
@@ -163,6 +173,15 @@ export function ChatList({ show }: { show?: boolean }) {
|
||||
<PlusCircle size={16} />
|
||||
<span>New Chat</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setIsSearchDialogOpen(!isSearchDialogOpen)}
|
||||
variant="outline"
|
||||
className="flex items-center justify-start gap-2 mx-2 py-3"
|
||||
data-testid="search-chats-button"
|
||||
>
|
||||
<Search size={16} />
|
||||
<span>Search chats</span>
|
||||
</Button>
|
||||
|
||||
{loading ? (
|
||||
<div className="py-3 px-4 text-sm text-gray-500">
|
||||
@@ -273,6 +292,15 @@ export function ChatList({ show }: { show?: boolean }) {
|
||||
onConfirmDelete={handleConfirmDelete}
|
||||
chatTitle={deleteChatTitle}
|
||||
/>
|
||||
|
||||
{/* Chat Search Dialog */}
|
||||
<ChatSearchDialog
|
||||
open={isSearchDialogOpen}
|
||||
onOpenChange={setIsSearchDialogOpen}
|
||||
onSelectChat={handleChatClick}
|
||||
appId={selectedAppId}
|
||||
allChats={chats}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user