Skip to content

Commit 804532d

Browse files
committed
feat: add collapsible reasoning blocks with auto-expand setting
- Added autoExpandReasoningBlocks configuration setting to control default expansion state - Created CollapsibleReasoningBlock component that wraps ReasoningBlock with collapsible UI - Updated ChatRow to use CollapsibleReasoningBlock instead of ReasoningBlock - Added preview text display when reasoning block is collapsed - Integrated with ExtensionStateContext for settings management Fixes #7873
1 parent 8fee312 commit 804532d

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

packages/types/src/global-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const globalSettingsSchema = z.object({
6767
alwaysAllowFollowupQuestions: z.boolean().optional(),
6868
followupAutoApproveTimeoutMs: z.number().optional(),
6969
alwaysAllowUpdateTodoList: z.boolean().optional(),
70+
autoExpandReasoningBlocks: z.boolean().optional(),
7071
allowedCommands: z.array(z.string()).optional(),
7172
deniedCommands: z.array(z.string()).optional(),
7273
commandExecutionTimeout: z.number().optional(),

src/core/webview/webviewMessageHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,10 @@ export const webviewMessageHandler = async (
15531553
await updateGlobalState("alwaysAllowFollowupQuestions", message.bool ?? false)
15541554
await provider.postStateToWebview()
15551555
break
1556+
case "autoExpandReasoningBlocks":
1557+
await updateGlobalState("autoExpandReasoningBlocks", message.bool ?? false)
1558+
await provider.postStateToWebview()
1559+
break
15561560
case "followupAutoApproveTimeoutMs":
15571561
await updateGlobalState("followupAutoApproveTimeoutMs", message.value)
15581562
await provider.postStateToWebview()

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export type ExtensionState = Pick<
229229
| "alwaysAllowExecute"
230230
| "alwaysAllowUpdateTodoList"
231231
| "followupAutoApproveTimeoutMs"
232+
| "autoExpandReasoningBlocks"
232233
| "allowedCommands"
233234
| "deniedCommands"
234235
| "allowedMaxRequests"

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export interface WebviewMessage {
4949
| "alwaysAllowFollowupQuestions"
5050
| "alwaysAllowUpdateTodoList"
5151
| "followupAutoApproveTimeoutMs"
52+
| "autoExpandReasoningBlocks"
5253
| "webviewDidLaunch"
5354
| "newTask"
5455
| "askResponse"

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import UpdateTodoListToolBlock from "./UpdateTodoListToolBlock"
2424
import CodeAccordian from "../common/CodeAccordian"
2525
import CodeBlock from "../common/CodeBlock"
2626
import MarkdownBlock from "../common/MarkdownBlock"
27-
import { ReasoningBlock } from "./ReasoningBlock"
27+
import { CollapsibleReasoningBlock } from "./CollapsibleReasoningBlock"
2828
import Thumbnails from "../common/Thumbnails"
2929
import ImageBlock from "../common/ImageBlock"
3030

@@ -1084,7 +1084,7 @@ export const ChatRowContent = ({
10841084
)
10851085
case "reasoning":
10861086
return (
1087-
<ReasoningBlock
1087+
<CollapsibleReasoningBlock
10881088
content={message.text || ""}
10891089
ts={message.ts}
10901090
isStreaming={isStreaming}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { useState, useEffect, useContext } from "react"
2+
import { ChevronDown, ChevronRight } from "lucide-react"
3+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible"
4+
import { ExtensionStateContext } from "../../context/ExtensionStateContext"
5+
import { ReasoningBlock } from "./ReasoningBlock"
6+
7+
interface CollapsibleReasoningBlockProps {
8+
content: string
9+
ts: number
10+
isStreaming: boolean
11+
isLast: boolean
12+
metadata?: any
13+
}
14+
15+
export const CollapsibleReasoningBlock: React.FC<CollapsibleReasoningBlockProps> = ({
16+
content,
17+
ts,
18+
isStreaming,
19+
isLast,
20+
metadata,
21+
}) => {
22+
const extensionState = useContext(ExtensionStateContext)
23+
const autoExpand = extensionState?.autoExpandReasoningBlocks ?? false
24+
25+
// Start with the configured default state
26+
const [isOpen, setIsOpen] = useState(autoExpand)
27+
28+
// Update when the setting changes
29+
useEffect(() => {
30+
setIsOpen(autoExpand)
31+
}, [autoExpand])
32+
33+
// Extract first line or preview of the reasoning content
34+
const getPreviewText = () => {
35+
if (!content) return "Thinking..."
36+
const lines = content.split("\n").filter((line) => line.trim())
37+
if (lines.length === 0) return "Thinking..."
38+
39+
// Get first meaningful line (skip empty lines)
40+
const firstLine = lines[0]
41+
const maxLength = 100
42+
43+
if (firstLine.length > maxLength) {
44+
return firstLine.substring(0, maxLength) + "..."
45+
}
46+
return firstLine + (lines.length > 1 ? "..." : "")
47+
}
48+
49+
return (
50+
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
51+
<div className="bg-vscode-editorWidget-background border border-vscode-editorWidget-border rounded-md overflow-hidden">
52+
<CollapsibleTrigger className="flex items-center justify-between w-full p-3 hover:bg-vscode-list-hoverBackground transition-colors">
53+
<div className="flex items-center gap-2">
54+
{isOpen ? (
55+
<ChevronDown className="h-4 w-4 text-vscode-descriptionForeground" />
56+
) : (
57+
<ChevronRight className="h-4 w-4 text-vscode-descriptionForeground" />
58+
)}
59+
<span className="text-sm font-medium text-vscode-descriptionForeground">Reasoning</span>
60+
{!isOpen && (
61+
<span className="text-sm text-vscode-descriptionForeground ml-2 truncate max-w-[500px]">
62+
{getPreviewText()}
63+
</span>
64+
)}
65+
</div>
66+
</CollapsibleTrigger>
67+
68+
<CollapsibleContent>
69+
<div className="border-t border-vscode-editorWidget-border">
70+
<ReasoningBlock
71+
content={content}
72+
ts={ts}
73+
isStreaming={isStreaming}
74+
isLast={isLast}
75+
metadata={metadata}
76+
/>
77+
</div>
78+
</CollapsibleContent>
79+
</div>
80+
</Collapsible>
81+
)
82+
}

0 commit comments

Comments
 (0)