mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-08 08:20:20 +08:00
* fix: prevent repeated content animation during thinking streaming (#614) - Implement chunked rendering using reasoningContentChunks - Static content (previous chunks) renders without animation - Only current streaming chunk animates - Disable animation on plan content (title, thought, steps) during streaming - Animation applies after content finishes streaming (when complete) - Prevents visual duplication of repeated sentences in thinking process
This commit is contained in:
@@ -314,11 +314,13 @@ function ThoughtBlock({
|
||||
content,
|
||||
isStreaming,
|
||||
hasMainContent,
|
||||
contentChunks,
|
||||
}: {
|
||||
className?: string;
|
||||
content: string;
|
||||
isStreaming?: boolean;
|
||||
hasMainContent?: boolean;
|
||||
contentChunks?: string[];
|
||||
}) {
|
||||
const t = useTranslations("chat.research");
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
@@ -336,6 +338,12 @@ function ThoughtBlock({
|
||||
return null;
|
||||
}
|
||||
|
||||
// Split content into static (previous chunks) and streaming (current chunk)
|
||||
const chunks = contentChunks ?? [];
|
||||
const staticContent = chunks.slice(0, -1).join("");
|
||||
const streamingChunk = isStreaming && chunks.length > 0 ? (chunks[chunks.length - 1] ?? "") : "";
|
||||
const hasStreamingContent = isStreaming && streamingChunk.length > 0;
|
||||
|
||||
return (
|
||||
<div className={cn("mb-6 w-full", className)}>
|
||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||
@@ -399,15 +407,39 @@ function ThoughtBlock({
|
||||
scrollShadow={false}
|
||||
autoScrollToBottom
|
||||
>
|
||||
<Markdown
|
||||
className={cn(
|
||||
"prose dark:prose-invert max-w-none transition-colors duration-200",
|
||||
isStreaming ? "prose-primary" : "opacity-80",
|
||||
)}
|
||||
animated={isStreaming}
|
||||
>
|
||||
{content}
|
||||
</Markdown>
|
||||
{staticContent && (
|
||||
<Markdown
|
||||
className={cn(
|
||||
"prose dark:prose-invert max-w-none transition-colors duration-200",
|
||||
"opacity-80",
|
||||
)}
|
||||
animated={false}
|
||||
>
|
||||
{staticContent}
|
||||
</Markdown>
|
||||
)}
|
||||
{hasStreamingContent && (
|
||||
<Markdown
|
||||
className={cn(
|
||||
"prose dark:prose-invert max-w-none transition-colors duration-200",
|
||||
"prose-primary",
|
||||
)}
|
||||
animated={true}
|
||||
>
|
||||
{streamingChunk}
|
||||
</Markdown>
|
||||
)}
|
||||
{!hasStreamingContent && (
|
||||
<Markdown
|
||||
className={cn(
|
||||
"prose dark:prose-invert max-w-none transition-colors duration-200",
|
||||
isStreaming ? "prose-primary" : "opacity-80",
|
||||
)}
|
||||
animated={false}
|
||||
>
|
||||
{content}
|
||||
</Markdown>
|
||||
)}
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -473,6 +505,7 @@ function PlanCard({
|
||||
content={reasoningContent}
|
||||
isStreaming={isThinking}
|
||||
hasMainContent={hasMainContent}
|
||||
contentChunks={message.reasoningContentChunks}
|
||||
/>
|
||||
)}
|
||||
{shouldShowPlan && (
|
||||
@@ -484,7 +517,7 @@ function PlanCard({
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
<Markdown animated={message.isStreaming}>
|
||||
<Markdown animated={false}>
|
||||
{`### ${
|
||||
plan.title !== undefined && plan.title !== ""
|
||||
? plan.title
|
||||
@@ -495,7 +528,7 @@ function PlanCard({
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div style={{ wordBreak: 'break-all', whiteSpace: 'normal' }}>
|
||||
<Markdown className="opacity-80" animated={message.isStreaming}>
|
||||
<Markdown className="opacity-80" animated={false}>
|
||||
{plan.thought}
|
||||
</Markdown>
|
||||
{plan.steps && (
|
||||
@@ -505,7 +538,7 @@ function PlanCard({
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="flex-1">
|
||||
<h3 className="mb flex items-center gap-2 text-lg font-medium">
|
||||
<Markdown animated={message.isStreaming}>
|
||||
<Markdown animated={false}>
|
||||
{step.title}
|
||||
</Markdown>
|
||||
{step.tools && step.tools.length > 0 && (
|
||||
@@ -520,7 +553,7 @@ function PlanCard({
|
||||
)}
|
||||
</h3>
|
||||
<div className="text-muted-foreground text-sm" style={{ wordBreak: 'break-all', whiteSpace: 'normal' }}>
|
||||
<Markdown animated={message.isStreaming}>
|
||||
<Markdown animated={false}>
|
||||
{step.description}
|
||||
</Markdown>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user