Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const globalSettingsSchema = z.object({
alwaysAllowFollowupQuestions: z.boolean().optional(),
followupAutoApproveTimeoutMs: z.number().optional(),
alwaysAllowUpdateTodoList: z.boolean().optional(),
autoExpandReasoningBlocks: z.boolean().optional(),
allowedCommands: z.array(z.string()).optional(),
deniedCommands: z.array(z.string()).optional(),
commandExecutionTimeout: z.number().optional(),
Expand Down
4 changes: 4 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,10 @@ export const webviewMessageHandler = async (
await updateGlobalState("alwaysAllowFollowupQuestions", message.bool ?? false)
await provider.postStateToWebview()
break
case "autoExpandReasoningBlocks":
await updateGlobalState("autoExpandReasoningBlocks", message.bool ?? false)
await provider.postStateToWebview()
break
case "followupAutoApproveTimeoutMs":
await updateGlobalState("followupAutoApproveTimeoutMs", message.value)
await provider.postStateToWebview()
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ export type ExtensionState = Pick<
| "alwaysAllowExecute"
| "alwaysAllowUpdateTodoList"
| "followupAutoApproveTimeoutMs"
| "autoExpandReasoningBlocks"
| "allowedCommands"
| "deniedCommands"
| "allowedMaxRequests"
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface WebviewMessage {
| "alwaysAllowFollowupQuestions"
| "alwaysAllowUpdateTodoList"
| "followupAutoApproveTimeoutMs"
| "autoExpandReasoningBlocks"
| "webviewDidLaunch"
| "newTask"
| "askResponse"
Expand Down
4 changes: 2 additions & 2 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import UpdateTodoListToolBlock from "./UpdateTodoListToolBlock"
import CodeAccordian from "../common/CodeAccordian"
import CodeBlock from "../common/CodeBlock"
import MarkdownBlock from "../common/MarkdownBlock"
import { ReasoningBlock } from "./ReasoningBlock"
import { CollapsibleReasoningBlock } from "./CollapsibleReasoningBlock"
import Thumbnails from "../common/Thumbnails"
import ImageBlock from "../common/ImageBlock"

Expand Down Expand Up @@ -1084,7 +1084,7 @@ export const ChatRowContent = ({
)
case "reasoning":
return (
<ReasoningBlock
<CollapsibleReasoningBlock
content={message.text || ""}
ts={message.ts}
isStreaming={isStreaming}
Expand Down
82 changes: 82 additions & 0 deletions webview-ui/src/components/chat/CollapsibleReasoningBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState, useEffect, useContext } from "react"
import { ChevronDown, ChevronRight } from "lucide-react"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible"
import { ExtensionStateContext } from "../../context/ExtensionStateContext"
import { ReasoningBlock } from "./ReasoningBlock"

interface CollapsibleReasoningBlockProps {
content: string
ts: number
isStreaming: boolean
isLast: boolean
metadata?: any
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type safety concern: Using any type here. Could we define a proper type for metadata or remove it if it's not being used?

}

export const CollapsibleReasoningBlock: React.FC<CollapsibleReasoningBlockProps> = ({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for this new component. Since this is a new UI component with state management and user interactions, shouldn't we add unit tests to ensure the collapsible behavior works correctly?

content,
ts,
isStreaming,
isLast,
metadata,
}) => {
const extensionState = useContext(ExtensionStateContext)
const autoExpand = extensionState?.autoExpandReasoningBlocks ?? false
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical issue: The new autoExpandReasoningBlocks setting isn't exposed in the Settings UI. Users have no way to toggle this feature through the interface. Could we add this to the AutoApproveSettings component or create a dedicated UI section for reasoning block preferences?


// Start with the configured default state
const [isOpen, setIsOpen] = useState(autoExpand)

// Update when the setting changes
useEffect(() => {
setIsOpen(autoExpand)
}, [autoExpand])

// Extract first line or preview of the reasoning content
const getPreviewText = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance optimization: The getPreviewText() function is called on every render when collapsed. Consider memoizing this with useMemo since it only depends on content:

if (!content) return "Thinking..."
const lines = content.split("\n").filter((line) => line.trim())
if (lines.length === 0) return "Thinking..."

// Get first meaningful line (skip empty lines)
const firstLine = lines[0]
const maxLength = 100

if (firstLine.length > maxLength) {
return firstLine.substring(0, maxLength) + "..."
}
return firstLine + (lines.length > 1 ? "..." : "")
}

return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="bg-vscode-editorWidget-background border border-vscode-editorWidget-border rounded-md overflow-hidden">
<CollapsibleTrigger className="flex items-center justify-between w-full p-3 hover:bg-vscode-list-hoverBackground transition-colors">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessibility improvement: Consider adding aria-expanded attribute for better screen reader support:

<div className="flex items-center gap-2">
{isOpen ? (
<ChevronDown className="h-4 w-4 text-vscode-descriptionForeground" />
) : (
<ChevronRight className="h-4 w-4 text-vscode-descriptionForeground" />
)}
<span className="text-sm font-medium text-vscode-descriptionForeground">Reasoning</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header label "Reasoning" is hardcoded. Consider wrapping it in a translation function to support multiple languages.

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Reasoning" label is hardcoded and not using i18n. For consistency with the rest of the UI, consider using:

{!isOpen && (
<span className="text-sm text-vscode-descriptionForeground ml-2 truncate max-w-[500px]">
{getPreviewText()}
</span>
)}
</div>
</CollapsibleTrigger>

<CollapsibleContent>
<div className="border-t border-vscode-editorWidget-border">
<ReasoningBlock
content={content}
ts={ts}
isStreaming={isStreaming}
isLast={isLast}
metadata={metadata}
/>
</div>
</CollapsibleContent>
</div>
</Collapsible>
)
}
Loading