This commit is contained in:
roomote-v0[bot] 2026-04-20 22:34:38 +03:00 • committed by GitHub
commit dc9a2b6fa7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 86 additions and 0 deletions

View file

@ -201,6 +201,11 @@ export const globalSettingsSchema = z.object({
includeTaskHistoryInEnhance: z.boolean().optional(), includeTaskHistoryInEnhance: z.boolean().optional(),
historyPreviewCollapsed: z.boolean().optional(), historyPreviewCollapsed: z.boolean().optional(),
reasoningBlockCollapsed: z.boolean().optional(), reasoningBlockCollapsed: z.boolean().optional(),
/**
* Whether to auto-expand diffs in "Roo wants to edit this file" chat messages.
* @default false
*/
autoExpandDiffs: z.boolean().optional(),
/** /**
* Controls the keyboard behavior for sending messages in the chat input. * Controls the keyboard behavior for sending messages in the chat input.
* - "send": Enter sends message, Shift+Enter creates newline (default) * - "send": Enter sends message, Shift+Enter creates newline (default)

View file

@ -299,6 +299,7 @@ export type ExtensionState = Pick<
| "openRouterImageGenerationSelectedModel" | "openRouterImageGenerationSelectedModel"
| "includeTaskHistoryInEnhance" | "includeTaskHistoryInEnhance"
| "reasoningBlockCollapsed" | "reasoningBlockCollapsed"
| "autoExpandDiffs"
| "enterBehavior" | "enterBehavior"
| "includeCurrentTime" | "includeCurrentTime"
| "includeCurrentCost" | "includeCurrentCost"

View file

@ -93,6 +93,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
cloudIsAuthenticated, cloudIsAuthenticated,
messageQueue = [], messageQueue = [],
showWorktreesInHomeScreen, showWorktreesInHomeScreen,
autoExpandDiffs,
} = useExtensionState() } = useExtensionState()
// Show a WarningRow when the user sends a message with a retired provider. // Show a WarningRow when the user sends a message with a retired provider.
@ -1261,6 +1262,49 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
return result return result
}, [isCondensing, visibleMessages]) }, [isCondensing, visibleMessages])
// Auto-expand diff tool messages when the autoExpandDiffs setting is enabled.
// This watches for new messages that contain file-edit diffs and marks them as expanded
// so users don't need to click on each collapsed diff block to review changes.
const DIFF_TOOL_NAMES = useMemo(
() =>
new Set([
"editedExistingFile",
"appliedDiff",
"newFileCreated",
"insertContent",
"searchAndReplace",
"search_and_replace",
]),
[],
)
useEffect(() => {
if (!autoExpandDiffs) return
const newExpansions: Record<number, boolean> = {}
for (const msg of groupedMessages) {
// Skip messages already tracked in expandedRows
if (expandedRows[msg.ts] !== undefined) continue
if (msg.type === "ask" && msg.ask === "tool") {
try {
const tool = JSON.parse(msg.text || "{}")
// Handle both single diff tools and batch diff messages
if (DIFF_TOOL_NAMES.has(tool.tool) || tool.tool === "batchDiffApproval") {
newExpansions[msg.ts] = true
}
} catch {
// ignore parse errors
}
}
}
if (Object.keys(newExpansions).length > 0) {
setExpandedRows((prev) => ({ ...prev, ...newExpansions }))
}
}, [autoExpandDiffs, groupedMessages, expandedRows, DIFF_TOOL_NAMES])
// Scroll lifecycle is managed by a dedicated hook to keep ChatView focused // Scroll lifecycle is managed by a dedicated hook to keep ChatView focused
// on message handling and UI orchestration. // on message handling and UI orchestration.
const { const {

View file

@ -199,6 +199,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
openRouterImageApiKey, openRouterImageApiKey,
openRouterImageGenerationSelectedModel, openRouterImageGenerationSelectedModel,
reasoningBlockCollapsed, reasoningBlockCollapsed,
autoExpandDiffs,
enterBehavior, enterBehavior,
includeCurrentTime, includeCurrentTime,
includeCurrentCost, includeCurrentCost,
@ -412,6 +413,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
followupAutoApproveTimeoutMs, followupAutoApproveTimeoutMs,
includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true, includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true,
reasoningBlockCollapsed: reasoningBlockCollapsed ?? true, reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
autoExpandDiffs: autoExpandDiffs ?? false,
enterBehavior: enterBehavior ?? "send", enterBehavior: enterBehavior ?? "send",
includeCurrentTime: includeCurrentTime ?? true, includeCurrentTime: includeCurrentTime ?? true,
includeCurrentCost: includeCurrentCost ?? true, includeCurrentCost: includeCurrentCost ?? true,
@ -891,6 +893,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
{renderTab === "ui" && ( {renderTab === "ui" && (
<UISettings <UISettings
reasoningBlockCollapsed={reasoningBlockCollapsed ?? true} reasoningBlockCollapsed={reasoningBlockCollapsed ?? true}
autoExpandDiffs={autoExpandDiffs ?? false}
enterBehavior={enterBehavior ?? "send"} enterBehavior={enterBehavior ?? "send"}
setCachedStateField={setCachedStateField} setCachedStateField={setCachedStateField}
/> />

View file

@ -11,12 +11,14 @@ import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
interface UISettingsProps extends HTMLAttributes<HTMLDivElement> { interface UISettingsProps extends HTMLAttributes<HTMLDivElement> {
reasoningBlockCollapsed: boolean reasoningBlockCollapsed: boolean
autoExpandDiffs: boolean
enterBehavior: "send" | "newline" enterBehavior: "send" | "newline"
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType> setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
} }
export const UISettings = ({ export const UISettings = ({
reasoningBlockCollapsed, reasoningBlockCollapsed,
autoExpandDiffs,
enterBehavior, enterBehavior,
setCachedStateField, setCachedStateField,
...props ...props
@ -38,6 +40,10 @@ export const UISettings = ({
}) })
} }
const handleAutoExpandDiffsChange = (value: boolean) => {
setCachedStateField("autoExpandDiffs", value)
}
const handleEnterBehaviorChange = (requireCtrlEnter: boolean) => { const handleEnterBehaviorChange = (requireCtrlEnter: boolean) => {
const newBehavior = requireCtrlEnter ? "newline" : "send" const newBehavior = requireCtrlEnter ? "newline" : "send"
setCachedStateField("enterBehavior", newBehavior) setCachedStateField("enterBehavior", newBehavior)
@ -72,6 +78,24 @@ export const UISettings = ({
</div> </div>
</SearchableSetting> </SearchableSetting>
{/* Auto-Expand Diffs Setting */}
<SearchableSetting
settingId="ui-auto-expand-diffs"
section="ui"
label={t("settings:ui.autoExpandDiffs.label")}>
<div className="flex flex-col gap-1">
<VSCodeCheckbox
checked={autoExpandDiffs}
onChange={(e: any) => handleAutoExpandDiffsChange(e.target.checked)}
data-testid="auto-expand-diffs-checkbox">
<span className="font-medium">{t("settings:ui.autoExpandDiffs.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm ml-5 mt-1">
{t("settings:ui.autoExpandDiffs.description")}
</div>
</div>
</SearchableSetting>
{/* Enter Key Behavior Setting */} {/* Enter Key Behavior Setting */}
<SearchableSetting <SearchableSetting
settingId="ui-enter-behavior" settingId="ui-enter-behavior"

View file

@ -302,6 +302,7 @@ describe("SettingsView - Change Detection Fix", () => {
openRouterImageApiKey: undefined, openRouterImageApiKey: undefined,
openRouterImageGenerationSelectedModel: undefined, openRouterImageGenerationSelectedModel: undefined,
reasoningBlockCollapsed: true, reasoningBlockCollapsed: true,
autoExpandDiffs: false,
...overrides, ...overrides,
}) })

View file

@ -307,6 +307,7 @@ describe("SettingsView - Unsaved Changes Detection", () => {
openRouterImageApiKey: undefined, openRouterImageApiKey: undefined,
openRouterImageGenerationSelectedModel: undefined, openRouterImageGenerationSelectedModel: undefined,
reasoningBlockCollapsed: true, reasoningBlockCollapsed: true,
autoExpandDiffs: false,
} }
beforeEach(() => { beforeEach(() => {

View file

@ -5,6 +5,7 @@ import { UISettings } from "../UISettings"
describe("UISettings", () => { describe("UISettings", () => {
const defaultProps = { const defaultProps = {
reasoningBlockCollapsed: false, reasoningBlockCollapsed: false,
autoExpandDiffs: false,
enterBehavior: "send" as const, enterBehavior: "send" as const,
setCachedStateField: vi.fn(), setCachedStateField: vi.fn(),
} }

View file

@ -235,6 +235,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
terminalZdotdir: false, // Default ZDOTDIR handling setting terminalZdotdir: false, // Default ZDOTDIR handling setting
historyPreviewCollapsed: false, // Initialize the new state (default to expanded) historyPreviewCollapsed: false, // Initialize the new state (default to expanded)
reasoningBlockCollapsed: true, // Default to collapsed reasoningBlockCollapsed: true, // Default to collapsed
autoExpandDiffs: false, // Default to collapsed diffs
enterBehavior: "send", // Default: Enter sends, Shift+Enter creates newline enterBehavior: "send", // Default: Enter sends, Shift+Enter creates newline
cloudUserInfo: null, cloudUserInfo: null,
cloudIsAuthenticated: false, cloudIsAuthenticated: false,
@ -488,6 +489,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
const contextValue: ExtensionStateContextType = { const contextValue: ExtensionStateContextType = {
...state, ...state,
reasoningBlockCollapsed: state.reasoningBlockCollapsed ?? true, reasoningBlockCollapsed: state.reasoningBlockCollapsed ?? true,
autoExpandDiffs: state.autoExpandDiffs ?? false,
didHydrateState, didHydrateState,
showWelcome, showWelcome,
theme, theme,

View file

@ -160,6 +160,10 @@
"label": "Collapse Thinking messages by default", "label": "Collapse Thinking messages by default",
"description": "When enabled, thinking blocks will be collapsed by default until you interact with them" "description": "When enabled, thinking blocks will be collapsed by default until you interact with them"
}, },
"autoExpandDiffs": {
"label": "Auto-expand diffs in chat messages",
"description": "When enabled, file edit diffs will be automatically expanded instead of collapsed behind the filename"
},
"requireCtrlEnterToSend": { "requireCtrlEnterToSend": {
"label": "Require {{primaryMod}}+Enter to send messages", "label": "Require {{primaryMod}}+Enter to send messages",
"description": "When enabled, you must press {{primaryMod}}+Enter to send messages instead of just Enter" "description": "When enabled, you must press {{primaryMod}}+Enter to send messages instead of just Enter"