// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // SPDX-License-Identifier: MIT import { Check, Copy, Headphones, Pencil, Undo2, X, Download } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { ScrollContainer } from "~/components/deer-flow/scroll-container"; import { Tooltip } from "~/components/deer-flow/tooltip"; import { Button } from "~/components/ui/button"; import { Card } from "~/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"; import { useReplay } from "~/core/replay"; import { closeResearch, listenToPodcast, useStore } from "~/core/store"; import { cn } from "~/lib/utils"; import { ResearchActivitiesBlock } from "./research-activities-block"; import { ResearchReportBlock } from "./research-report-block"; export function ResearchBlock({ className, researchId = null, }: { className?: string; researchId: string | null; }) { const reportId = useStore((state) => researchId ? state.researchReportIds.get(researchId) : undefined, ); const [activeTab, setActiveTab] = useState("activities"); const hasReport = useStore((state) => researchId ? state.researchReportIds.has(researchId) : false, ); const reportStreaming = useStore((state) => reportId ? (state.messages.get(reportId)?.isStreaming ?? false) : false, ); const { isReplay } = useReplay(); useEffect(() => { if (hasReport) { setActiveTab("report"); } }, [hasReport]); const handleGeneratePodcast = useCallback(async () => { if (!researchId) { return; } await listenToPodcast(researchId); }, [researchId]); const [editing, setEditing] = useState(false); 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]); // Download report as markdown const handleDownload = useCallback(() => { if (!reportId) { return; } const report = useStore.getState().messages.get(reportId); if (!report) { return; } const now = new Date(); const pad = (n: number) => n.toString().padStart(2, '0'); const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`; const filename = `research-report-${timestamp}.md`; const blob = new Blob([report.content], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 0); }, [reportId]); const handleEdit = useCallback(() => { setEditing((editing) => !editing); }, []); // When the research id changes, set the active tab to activities useEffect(() => { if (!hasReport) { setActiveTab("activities"); } }, [hasReport, researchId]); return (