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 return (
{expanded && (
{content} {showCursor && }
)}
) } /** Shared keyframe styles injected once per ThinkingBlock mount */ const thinkingStyles = ` @keyframes thinking-cursor-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes thinking-dots { 0%, 20% { content: ''; } 40% { content: '.'; } 60% { content: '..'; } 80%, 100% { content: '...'; } } .thinking-cursor { display: inline-block; width: 2px; height: 1em; background: var(--color-text-tertiary); vertical-align: middle; margin-left: 1px; animation: thinking-cursor-blink 1s step-end infinite; } .thinking-inline-cursor { display: inline-block; width: 1px; height: 0.95em; margin-left: 3px; vertical-align: text-bottom; background: var(--color-text-tertiary); animation: thinking-cursor-blink 1s step-end infinite; } .thinking-dots::after { content: ''; animation: thinking-dots 1.4s steps(1, end) infinite; } `