Skip to content

Commit 0ac5f16

Browse files
committed
feat: add autoExpandDiffs setting to auto-expand diffs in chat messages
Adds a new boolean setting "autoExpandDiffs" (default: false) under Settings > UI. When enabled, file edit diffs in chat messages are automatically expanded instead of requiring a click on the collapsed filename bar. The CodeAccordion component already enforces a 300px max height with scrollbar, so auto-expanded diffs will not overwhelm the chat view. Changes: - packages/types: add autoExpandDiffs to GlobalSettings schema and ExtensionState - webview-ui context: add default and hydration for autoExpandDiffs - ChatView: add useEffect that auto-expands diff tool messages when setting is on - UISettings: add checkbox toggle for the new setting - SettingsView: wire the new setting through cached state - i18n: add English translation strings - Tests: update test fixtures for change-detection, unsaved-changes, UISettings Closes #10955
1 parent cb83656 commit 0ac5f16

10 files changed

Lines changed: 86 additions & 0 deletions

File tree

packages/types/src/global-settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ export const globalSettingsSchema = z.object({
201201
includeTaskHistoryInEnhance: z.boolean().optional(),
202202
historyPreviewCollapsed: z.boolean().optional(),
203203
reasoningBlockCollapsed: z.boolean().optional(),
204+
/**
205+
* Whether to auto-expand diffs in "Roo wants to edit this file" chat messages.
206+
* @default false
207+
*/
208+
autoExpandDiffs: z.boolean().optional(),
204209
/**
205210
* Controls the keyboard behavior for sending messages in the chat input.
206211
* - "send": Enter sends message, Shift+Enter creates newline (default)

packages/types/src/vscode-extension-host.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ export type ExtensionState = Pick<
299299
| "openRouterImageGenerationSelectedModel"
300300
| "includeTaskHistoryInEnhance"
301301
| "reasoningBlockCollapsed"
302+
| "autoExpandDiffs"
302303
| "enterBehavior"
303304
| "includeCurrentTime"
304305
| "includeCurrentCost"

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
9393
cloudIsAuthenticated,
9494
messageQueue = [],
9595
showWorktreesInHomeScreen,
96+
autoExpandDiffs,
9697
} = useExtensionState()
9798

9899
// Show a WarningRow when the user sends a message with a retired provider.
@@ -1261,6 +1262,49 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
12611262
return result
12621263
}, [isCondensing, visibleMessages])
12631264

1265+
// Auto-expand diff tool messages when the autoExpandDiffs setting is enabled.
1266+
// This watches for new messages that contain file-edit diffs and marks them as expanded
1267+
// so users don't need to click on each collapsed diff block to review changes.
1268+
const DIFF_TOOL_NAMES = useMemo(
1269+
() =>
1270+
new Set([
1271+
"editedExistingFile",
1272+
"appliedDiff",
1273+
"newFileCreated",
1274+
"insertContent",
1275+
"searchAndReplace",
1276+
"search_and_replace",
1277+
]),
1278+
[],
1279+
)
1280+
1281+
useEffect(() => {
1282+
if (!autoExpandDiffs) return
1283+
1284+
const newExpansions: Record<number, boolean> = {}
1285+
1286+
for (const msg of groupedMessages) {
1287+
// Skip messages already tracked in expandedRows
1288+
if (expandedRows[msg.ts] !== undefined) continue
1289+
1290+
if (msg.type === "ask" && msg.ask === "tool") {
1291+
try {
1292+
const tool = JSON.parse(msg.text || "{}")
1293+
// Handle both single diff tools and batch diff messages
1294+
if (DIFF_TOOL_NAMES.has(tool.tool) || tool.tool === "batchDiffApproval") {
1295+
newExpansions[msg.ts] = true
1296+
}
1297+
} catch {
1298+
// ignore parse errors
1299+
}
1300+
}
1301+
}
1302+
1303+
if (Object.keys(newExpansions).length > 0) {
1304+
setExpandedRows((prev) => ({ ...prev, ...newExpansions }))
1305+
}
1306+
}, [autoExpandDiffs, groupedMessages, expandedRows, DIFF_TOOL_NAMES])
1307+
12641308
// Scroll lifecycle is managed by a dedicated hook to keep ChatView focused
12651309
// on message handling and UI orchestration.
12661310
const {

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
199199
openRouterImageApiKey,
200200
openRouterImageGenerationSelectedModel,
201201
reasoningBlockCollapsed,
202+
autoExpandDiffs,
202203
enterBehavior,
203204
includeCurrentTime,
204205
includeCurrentCost,
@@ -412,6 +413,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
412413
followupAutoApproveTimeoutMs,
413414
includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true,
414415
reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
416+
autoExpandDiffs: autoExpandDiffs ?? false,
415417
enterBehavior: enterBehavior ?? "send",
416418
includeCurrentTime: includeCurrentTime ?? true,
417419
includeCurrentCost: includeCurrentCost ?? true,
@@ -891,6 +893,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
891893
{renderTab === "ui" && (
892894
<UISettings
893895
reasoningBlockCollapsed={reasoningBlockCollapsed ?? true}
896+
autoExpandDiffs={autoExpandDiffs ?? false}
894897
enterBehavior={enterBehavior ?? "send"}
895898
setCachedStateField={setCachedStateField}
896899
/>

webview-ui/src/components/settings/UISettings.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
1111

1212
interface UISettingsProps extends HTMLAttributes<HTMLDivElement> {
1313
reasoningBlockCollapsed: boolean
14+
autoExpandDiffs: boolean
1415
enterBehavior: "send" | "newline"
1516
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
1617
}
1718

1819
export const UISettings = ({
1920
reasoningBlockCollapsed,
21+
autoExpandDiffs,
2022
enterBehavior,
2123
setCachedStateField,
2224
...props
@@ -38,6 +40,10 @@ export const UISettings = ({
3840
})
3941
}
4042

43+
const handleAutoExpandDiffsChange = (value: boolean) => {
44+
setCachedStateField("autoExpandDiffs", value)
45+
}
46+
4147
const handleEnterBehaviorChange = (requireCtrlEnter: boolean) => {
4248
const newBehavior = requireCtrlEnter ? "newline" : "send"
4349
setCachedStateField("enterBehavior", newBehavior)
@@ -72,6 +78,24 @@ export const UISettings = ({
7278
</div>
7379
</SearchableSetting>
7480

81+
{/* Auto-Expand Diffs Setting */}
82+
<SearchableSetting
83+
settingId="ui-auto-expand-diffs"
84+
section="ui"
85+
label={t("settings:ui.autoExpandDiffs.label")}>
86+
<div className="flex flex-col gap-1">
87+
<VSCodeCheckbox
88+
checked={autoExpandDiffs}
89+
onChange={(e: any) => handleAutoExpandDiffsChange(e.target.checked)}
90+
data-testid="auto-expand-diffs-checkbox">
91+
<span className="font-medium">{t("settings:ui.autoExpandDiffs.label")}</span>
92+
</VSCodeCheckbox>
93+
<div className="text-vscode-descriptionForeground text-sm ml-5 mt-1">
94+
{t("settings:ui.autoExpandDiffs.description")}
95+
</div>
96+
</div>
97+
</SearchableSetting>
98+
7599
{/* Enter Key Behavior Setting */}
76100
<SearchableSetting
77101
settingId="ui-enter-behavior"

webview-ui/src/components/settings/__tests__/SettingsView.change-detection.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ describe("SettingsView - Change Detection Fix", () => {
302302
openRouterImageApiKey: undefined,
303303
openRouterImageGenerationSelectedModel: undefined,
304304
reasoningBlockCollapsed: true,
305+
autoExpandDiffs: false,
305306
...overrides,
306307
})
307308

webview-ui/src/components/settings/__tests__/SettingsView.unsaved-changes.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ describe("SettingsView - Unsaved Changes Detection", () => {
307307
openRouterImageApiKey: undefined,
308308
openRouterImageGenerationSelectedModel: undefined,
309309
reasoningBlockCollapsed: true,
310+
autoExpandDiffs: false,
310311
}
311312

312313
beforeEach(() => {

webview-ui/src/components/settings/__tests__/UISettings.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { UISettings } from "../UISettings"
55
describe("UISettings", () => {
66
const defaultProps = {
77
reasoningBlockCollapsed: false,
8+
autoExpandDiffs: false,
89
enterBehavior: "send" as const,
910
setCachedStateField: vi.fn(),
1011
}

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
235235
terminalZdotdir: false, // Default ZDOTDIR handling setting
236236
historyPreviewCollapsed: false, // Initialize the new state (default to expanded)
237237
reasoningBlockCollapsed: true, // Default to collapsed
238+
autoExpandDiffs: false, // Default to collapsed diffs
238239
enterBehavior: "send", // Default: Enter sends, Shift+Enter creates newline
239240
cloudUserInfo: null,
240241
cloudIsAuthenticated: false,
@@ -488,6 +489,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
488489
const contextValue: ExtensionStateContextType = {
489490
...state,
490491
reasoningBlockCollapsed: state.reasoningBlockCollapsed ?? true,
492+
autoExpandDiffs: state.autoExpandDiffs ?? false,
491493
didHydrateState,
492494
showWelcome,
493495
theme,

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@
160160
"label": "Collapse Thinking messages by default",
161161
"description": "When enabled, thinking blocks will be collapsed by default until you interact with them"
162162
},
163+
"autoExpandDiffs": {
164+
"label": "Auto-expand diffs in chat messages",
165+
"description": "When enabled, file edit diffs will be automatically expanded instead of collapsed behind the filename"
166+
},
163167
"requireCtrlEnterToSend": {
164168
"label": "Require {{primaryMod}}+Enter to send messages",
165169
"description": "When enabled, you must press {{primaryMod}}+Enter to send messages instead of just Enter"

0 commit comments

Comments
 (0)