+ {/* 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; }