mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-07 20:48:43 +00:00
fix(frontend): make recent chat rows fully clickable (#3733)
* fix(frontend): make recent chat rows fully clickable * Address recent chat row review comments
This commit is contained in:
parent
cc1df2d038
commit
11415875c4
@ -241,95 +241,91 @@ export function RecentChatList() {
|
|||||||
className="group/side-menu-item"
|
className="group/side-menu-item"
|
||||||
>
|
>
|
||||||
<SidebarMenuButton isActive={isActive} asChild>
|
<SidebarMenuButton isActive={isActive} asChild>
|
||||||
<div>
|
<Link
|
||||||
<Link
|
className="text-muted-foreground min-w-0 whitespace-nowrap group-hover/side-menu-item:overflow-hidden"
|
||||||
className="text-muted-foreground flex min-w-0 items-center gap-1.5 pr-7 whitespace-nowrap group-hover/side-menu-item:overflow-hidden"
|
href={pathOfThread(thread)}
|
||||||
href={pathOfThread(thread)}
|
>
|
||||||
>
|
<ThreadChannelIcon source={channelSource} />
|
||||||
<ThreadChannelIcon source={channelSource} />
|
<span className="min-w-0 truncate">
|
||||||
<span className="min-w-0 truncate">
|
{titleOfThread(thread)}
|
||||||
{titleOfThread(thread)}
|
</span>
|
||||||
</span>
|
{channelSource && (
|
||||||
{channelSource && (
|
<span
|
||||||
<span
|
className="bg-muted text-muted-foreground ml-auto inline-flex h-5 max-w-14 shrink-0 items-center rounded-md px-1.5 text-[10px] font-medium"
|
||||||
className="bg-muted text-muted-foreground ml-auto inline-flex h-5 max-w-14 shrink-0 items-center rounded-md px-1.5 text-[10px] font-medium"
|
title={`${channelSource.label} channel`}
|
||||||
title={`${channelSource.label} channel`}
|
>
|
||||||
>
|
<span className="truncate">
|
||||||
<span className="truncate">
|
{channelSource.label}
|
||||||
{channelSource.label}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
</span>
|
||||||
</Link>
|
)}
|
||||||
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true" && (
|
</Link>
|
||||||
<DropdownMenu>
|
</SidebarMenuButton>
|
||||||
<DropdownMenuTrigger asChild>
|
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true" && (
|
||||||
<SidebarMenuAction
|
<DropdownMenu>
|
||||||
showOnHover
|
<DropdownMenuTrigger asChild>
|
||||||
className="bg-background/50 hover:bg-background"
|
<SidebarMenuAction
|
||||||
>
|
showOnHover
|
||||||
<MoreHorizontal />
|
className="bg-background/50 hover:bg-background after:left-0!"
|
||||||
<span className="sr-only">{t.common.more}</span>
|
>
|
||||||
</SidebarMenuAction>
|
<MoreHorizontal />
|
||||||
</DropdownMenuTrigger>
|
<span className="sr-only">{t.common.more}</span>
|
||||||
<DropdownMenuContent
|
</SidebarMenuAction>
|
||||||
className="w-48 rounded-lg"
|
</DropdownMenuTrigger>
|
||||||
side={"right"}
|
<DropdownMenuContent
|
||||||
align={"start"}
|
className="w-48 rounded-lg"
|
||||||
>
|
side={"right"}
|
||||||
|
align={"start"}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() =>
|
||||||
|
handleRenameClick(
|
||||||
|
thread.thread_id,
|
||||||
|
titleOfThread(thread),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Pencil className="text-muted-foreground" />
|
||||||
|
<span>{t.common.rename}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() => handleShare(thread)}
|
||||||
|
>
|
||||||
|
<Share2 className="text-muted-foreground" />
|
||||||
|
<span>{t.common.share}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
<Download className="text-muted-foreground" />
|
||||||
|
<span>{t.common.export}</span>
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={() =>
|
onSelect={() =>
|
||||||
handleRenameClick(
|
handleExport(thread, "markdown")
|
||||||
thread.thread_id,
|
|
||||||
titleOfThread(thread),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Pencil className="text-muted-foreground" />
|
<FileText className="text-muted-foreground" />
|
||||||
<span>{t.common.rename}</span>
|
<span>{t.common.exportAsMarkdown}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={() => handleShare(thread)}
|
onSelect={() => handleExport(thread, "json")}
|
||||||
>
|
>
|
||||||
<Share2 className="text-muted-foreground" />
|
<FileJson className="text-muted-foreground" />
|
||||||
<span>{t.common.share}</span>
|
<span>{t.common.exportAsJSON}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSub>
|
</DropdownMenuSubContent>
|
||||||
<DropdownMenuSubTrigger>
|
</DropdownMenuSub>
|
||||||
<Download className="text-muted-foreground" />
|
<DropdownMenuSeparator />
|
||||||
<span>{t.common.export}</span>
|
<DropdownMenuItem
|
||||||
</DropdownMenuSubTrigger>
|
onSelect={() => handleDelete(thread)}
|
||||||
<DropdownMenuSubContent>
|
>
|
||||||
<DropdownMenuItem
|
<Trash2 className="text-muted-foreground" />
|
||||||
onSelect={() =>
|
<span>{t.common.delete}</span>
|
||||||
handleExport(thread, "markdown")
|
</DropdownMenuItem>
|
||||||
}
|
</DropdownMenuContent>
|
||||||
>
|
</DropdownMenu>
|
||||||
<FileText className="text-muted-foreground" />
|
)}
|
||||||
<span>{t.common.exportAsMarkdown}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onSelect={() =>
|
|
||||||
handleExport(thread, "json")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FileJson className="text-muted-foreground" />
|
|
||||||
<span>{t.common.exportAsJSON}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
onSelect={() => handleDelete(thread)}
|
|
||||||
>
|
|
||||||
<Trash2 className="text-muted-foreground" />
|
|
||||||
<span>{t.common.delete}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -53,6 +53,35 @@ test.describe("Thread history", () => {
|
|||||||
await expect(page).toHaveURL(new RegExp(MOCK_THREAD_ID));
|
await expect(page).toHaveURL(new RegExp(MOCK_THREAD_ID));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("clicking blank space in a sidebar thread row navigates to it", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
mockLangGraphAPI(page, { threads: THREADS });
|
||||||
|
|
||||||
|
await page.goto("/workspace/chats/new");
|
||||||
|
|
||||||
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
||||||
|
const firstThreadItem = sidebar
|
||||||
|
.locator("[data-sidebar='menu-item']")
|
||||||
|
.filter({ hasText: "First conversation" })
|
||||||
|
.first();
|
||||||
|
await expect(firstThreadItem).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
const firstThreadLink = firstThreadItem.getByRole("link");
|
||||||
|
await expect(firstThreadLink).toBeVisible();
|
||||||
|
|
||||||
|
const box = await firstThreadLink.boundingBox();
|
||||||
|
expect(box).not.toBeNull();
|
||||||
|
if (!box) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await firstThreadLink.click({ position: { x: 4, y: box.height / 2 } });
|
||||||
|
|
||||||
|
await page.waitForURL(`**/workspace/chats/${MOCK_THREAD_ID}`);
|
||||||
|
await expect(page).toHaveURL(new RegExp(MOCK_THREAD_ID));
|
||||||
|
});
|
||||||
|
|
||||||
test("existing thread loads historical messages", async ({ page }) => {
|
test("existing thread loads historical messages", async ({ page }) => {
|
||||||
mockLangGraphAPI(page, { threads: THREADS });
|
mockLangGraphAPI(page, { threads: THREADS });
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user