mirror of
https://git.mirrors.martin98.com/https://github.com/bytedance/deer-flow
synced 2025-08-18 07:46:00 +08:00
feat: use toolbar instead of single button
This commit is contained in:
parent
a970ab2b32
commit
9c9afb383b
@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
import { X } from "lucide-react";
|
import { Check, Copy, Headphones, X } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card } from "~/components/ui/card";
|
import { Card } from "~/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
||||||
import { openResearch, useStore } from "~/core/store";
|
import { listenToPodcast, openResearch, useStore } from "~/core/store";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
import { ResearchActivitiesBlock } from "./research-activities-block";
|
import { ResearchActivitiesBlock } from "./research-activities-block";
|
||||||
@ -29,16 +29,66 @@ export function ResearchBlock({
|
|||||||
const hasReport = useStore((state) =>
|
const hasReport = useStore((state) =>
|
||||||
researchId ? state.researchReportIds.has(researchId) : false,
|
researchId ? state.researchReportIds.has(researchId) : false,
|
||||||
);
|
);
|
||||||
|
const reportStreaming = useStore((state) =>
|
||||||
|
reportId ? (state.messages.get(reportId)?.isStreaming ?? false) : false,
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasReport) {
|
if (hasReport) {
|
||||||
setActiveTab("report");
|
setActiveTab("report");
|
||||||
}
|
}
|
||||||
}, [hasReport]);
|
}, [hasReport]);
|
||||||
|
|
||||||
|
const handleGeneratePodcast = useCallback(async () => {
|
||||||
|
if (!researchId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await listenToPodcast(researchId);
|
||||||
|
}, [researchId]);
|
||||||
|
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const handleCopy = useCallback(() => {
|
||||||
|
if (!reportId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const report = useStore.getState().messages.get(reportId);
|
||||||
|
if (!report) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void navigator.clipboard.writeText(report.content);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopied(false);
|
||||||
|
}, 1000);
|
||||||
|
}, [reportId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("h-full w-full", className)}>
|
<div className={cn("h-full w-full", className)}>
|
||||||
<Card className={cn("relative h-full w-full pt-4", className)}>
|
<Card className={cn("relative h-full w-full pt-4", className)}>
|
||||||
<div className="absolute right-4 flex h-9 items-center">
|
<div className="absolute right-4 flex h-9 items-center justify-center">
|
||||||
|
{hasReport && !reportStreaming && (
|
||||||
|
<>
|
||||||
|
<Tooltip title="Generate podcast">
|
||||||
|
<Button
|
||||||
|
className="text-gray-400"
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={handleGeneratePodcast}
|
||||||
|
>
|
||||||
|
<Headphones />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Copy">
|
||||||
|
<Button
|
||||||
|
className="text-gray-400"
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={handleCopy}
|
||||||
|
>
|
||||||
|
{copied ? <Check /> : <Copy />}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Tooltip title="Close">
|
<Tooltip title="Close">
|
||||||
<Button
|
<Button
|
||||||
className="text-gray-400"
|
className="text-gray-400"
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
import { Podcast } from "lucide-react";
|
import { useRef } from "react";
|
||||||
import { useCallback, useRef, useState } from "react";
|
|
||||||
|
|
||||||
import { Button } from "~/components/ui/button";
|
import { useMessage } from "~/core/store";
|
||||||
import { listenToPodcast, useMessage } from "~/core/store";
|
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
import { LoadingAnimation } from "./loading-animation";
|
import { LoadingAnimation } from "./loading-animation";
|
||||||
import { Markdown } from "./markdown";
|
import { Markdown } from "./markdown";
|
||||||
import { Tooltip } from "./tooltip";
|
|
||||||
|
|
||||||
export function ResearchReportBlock({
|
export function ResearchReportBlock({
|
||||||
className,
|
className,
|
||||||
researchId,
|
|
||||||
messageId,
|
messageId,
|
||||||
}: {
|
}: {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -23,39 +19,11 @@ export function ResearchReportBlock({
|
|||||||
}) {
|
}) {
|
||||||
const message = useMessage(messageId);
|
const message = useMessage(messageId);
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
const [isGenerated, setGenerated] = useState(false);
|
|
||||||
const handleListenToReport = useCallback(async () => {
|
|
||||||
if (isGenerated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setGenerated(true);
|
|
||||||
await listenToPodcast(researchId);
|
|
||||||
}, [isGenerated, researchId]);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className={cn("relative flex flex-col pb-8", className)}
|
className={cn("relative flex flex-col pb-8", className)}
|
||||||
>
|
>
|
||||||
<div className="absolute top-2 right-6">
|
|
||||||
{message?.content && !message.isStreaming && (
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
isGenerated ? "The podcast is generated" : "Generate podcast"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
disabled={isGenerated}
|
|
||||||
onClick={() => {
|
|
||||||
void handleListenToReport();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Podcast />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Markdown animate>{message?.content}</Markdown>
|
<Markdown animate>{message?.content}</Markdown>
|
||||||
{message?.isStreaming && <LoadingAnimation className="my-12" />}
|
{message?.isStreaming && <LoadingAnimation className="my-12" />}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user