From d25be78f0ccbd00d12b0da44ee7c6f1c1349b877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Mon, 6 Apr 2026 21:38:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20streamline=20AssistantMessage,=20Thinkin?= =?UTF-8?q?gBlock,=20and=20StreamingIndicator=20=E2=80=94=20remove=20avata?= =?UTF-8?q?r,=20compact=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/chat/AssistantMessage.tsx | 32 +++++--------- .../components/chat/StreamingIndicator.tsx | 20 +++------ desktop/src/components/chat/ThinkingBlock.tsx | 43 ++++++++----------- 3 files changed, 36 insertions(+), 59 deletions(-) diff --git a/desktop/src/components/chat/AssistantMessage.tsx b/desktop/src/components/chat/AssistantMessage.tsx index 36359f4f..62a8a622 100644 --- a/desktop/src/components/chat/AssistantMessage.tsx +++ b/desktop/src/components/chat/AssistantMessage.tsx @@ -20,27 +20,17 @@ export function AssistantMessage({ content, isStreaming }: Props) { } return ( -
- {/* Avatar */} -
- - - - - -
- {/* Content */} -
- {!isStreaming && content.trim() && ( -
- -
- )} +
+ {/* Copy button — absolute positioned, no reserved space */} + {!isStreaming && content.trim() && ( + + )} +
{isStreaming && ( diff --git a/desktop/src/components/chat/StreamingIndicator.tsx b/desktop/src/components/chat/StreamingIndicator.tsx index c1826d1f..5a8a2f05 100644 --- a/desktop/src/components/chat/StreamingIndicator.tsx +++ b/desktop/src/components/chat/StreamingIndicator.tsx @@ -1,26 +1,18 @@ import { useChatStore } from '../../stores/chatStore' export function StreamingIndicator() { - const { chatState, elapsedSeconds, tokenUsage } = useChatStore() + const { chatState } = useChatStore() const verb = chatState === 'thinking' ? 'Thinking' : chatState === 'tool_executing' - ? 'Executing' - : 'Crafting' + ? 'Running' + : 'Working' return ( -
- - {verb}... - - {elapsedSeconds}s - - {tokenUsage.output_tokens > 0 && ( - - · ↓ {tokenUsage.output_tokens} tokens - - )} +
+ + {verb}...
) } diff --git a/desktop/src/components/chat/ThinkingBlock.tsx b/desktop/src/components/chat/ThinkingBlock.tsx index 191cc82e..94481f02 100644 --- a/desktop/src/components/chat/ThinkingBlock.tsx +++ b/desktop/src/components/chat/ThinkingBlock.tsx @@ -2,58 +2,53 @@ import { useState, useEffect, useRef } from 'react' export function ThinkingBlock({ content, isActive = false }: { content: string; isActive?: boolean }) { const [expanded, setExpanded] = useState(false) - - // Auto-scroll to bottom of content area when expanded and content is still growing const contentRef = useRef(null) + useEffect(() => { if (expanded && isActive && contentRef.current) { contentRef.current.scrollTop = contentRef.current.scrollHeight } }, [content, expanded, isActive]) - const showCursor = isActive && expanded - const inlinePreview = content.replace(/\s+/g, ' ').trim() - const collapsedText = inlinePreview.slice(0, 140) - const hasOverflow = inlinePreview.length > collapsedText.length + // Preview: take first meaningful line, not first 140 chars + const lines = content.split('\n').filter((l) => l.trim()) + const firstLine = lines[0]?.replace(/\s+/g, ' ').trim() || '' + const preview = firstLine.length > 80 ? firstLine.slice(0, 80) + '...' : firstLine return ( -
+
{expanded && ( -
-
- {content} - {showCursor && } -
+
+ {content} + {isActive && expanded && }
)}
) } -/** Shared keyframe styles injected once per ThinkingBlock mount */ const thinkingStyles = ` @keyframes thinking-cursor-blink { 0%, 100% { opacity: 1; }