fix: add collapsible functionality to ReasoningBlock for better performance

- Added expand/collapse functionality to ReasoningBlock component
- Integrated with existing ChatRow expand/collapse state management
- Uses ToolUseBlock for consistent UI styling
- Improves performance by reducing DOM rendering for large thinking sections
- Fixes issue #8066 where reasoning sections caused sluggish UI performance
This commit is contained in:
Roo Code 2025-09-17 13:10:18 +00:00
parent dcc6db00c7
commit 66598d5264
2 changed files with 37 additions and 13 deletions

View file

@ -1090,6 +1090,8 @@ export const ChatRowContent = ({
isStreaming={isStreaming}
isLast={isLast}
metadata={message.metadata as any}
isExpanded={isExpanded}
onToggleExpand={handleToggleExpand}
/>
)
case "api_req_started":

View file

@ -2,7 +2,8 @@ import React, { useEffect, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
import MarkdownBlock from "../common/MarkdownBlock"
import { Clock, Lightbulb } from "lucide-react"
import { Clock, Lightbulb, ChevronDown, ChevronUp } from "lucide-react"
import { ToolUseBlock } from "../common/ToolUseBlock"
interface ReasoningBlockProps {
content: string
@ -10,19 +11,33 @@ interface ReasoningBlockProps {
isStreaming: boolean
isLast: boolean
metadata?: any
isExpanded?: boolean
onToggleExpand?: () => void
}
/**
* Render reasoning with a heading and a simple timer.
* - Heading uses i18n key chat:reasoning.thinking
* - Timer runs while reasoning is active (no persistence)
* - Content can be collapsed/expanded for better performance
*/
export const ReasoningBlock = ({ content, isStreaming, isLast }: ReasoningBlockProps) => {
export const ReasoningBlock = ({
content,
isStreaming,
isLast,
isExpanded: externalIsExpanded,
onToggleExpand,
}: ReasoningBlockProps) => {
const { t } = useTranslation()
const startTimeRef = useRef<number>(Date.now())
const [elapsed, setElapsed] = useState<number>(0)
// Use internal state if no external control is provided
const [internalIsExpanded, setInternalIsExpanded] = useState(false)
const isExpanded = externalIsExpanded !== undefined ? externalIsExpanded : internalIsExpanded
const toggleExpand = onToggleExpand || (() => setInternalIsExpanded(!internalIsExpanded))
// Simple timer that runs while streaming
useEffect(() => {
if (isLast && isStreaming) {
@ -35,26 +50,33 @@ export const ReasoningBlock = ({ content, isStreaming, isLast }: ReasoningBlockP
const seconds = Math.floor(elapsed / 1000)
const secondsLabel = t("chat:reasoning.seconds", { count: seconds })
const hasContent = (content?.trim()?.length ?? 0) > 0
return (
<div className="py-1">
<div className="flex items-center justify-between mb-2.5 pr-2">
<ToolUseBlock>
<div
className="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-vscode-list-hoverBackground"
onClick={toggleExpand}>
<div className="flex items-center gap-2">
<Lightbulb className="w-4" />
<span className="font-bold text-vscode-foreground">{t("chat:reasoning.thinking")}</span>
</div>
{elapsed > 0 && (
<span className="text-vscode-foreground tabular-nums flex items-center gap-1">
<Clock className="w-4" />
{secondsLabel}
</span>
)}
<div className="flex items-center gap-2">
{elapsed > 0 && (
<span className="text-vscode-foreground tabular-nums flex items-center gap-1">
<Clock className="w-4" />
{secondsLabel}
</span>
)}
{hasContent &&
(isExpanded ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />)}
</div>
</div>
{(content?.trim()?.length ?? 0) > 0 && (
<div className="px-3 italic text-vscode-descriptionForeground">
{hasContent && isExpanded && (
<div className="px-4 py-2 italic text-vscode-descriptionForeground border-t border-vscode-panel-border">
<MarkdownBlock markdown={content} />
</div>
)}
</div>
</ToolUseBlock>
)
}